分类 technique 下的文章

x86-64

Loading a value into a 32-bit register name sets the upper 32 bits of the register to zero. Thus, after movl $-1, %eax, the %rax register has value 0x00000000FFFFFFFF.
Loading a value into a 16- or 8-bit register name leaves all other bits unchanged.
既movl等操作32bit指令会将后32bit置零

防止假期摸鱼,记一下每天都干了什么
这个假期主要需要搞TVM那边的工作

6.11

$SHELL环境变量是当前的shell路径

6.12

tmux调整当前窗口全屏 : ctrl+b z
tmux新建窗口: ctrl+b c ,下一个窗口:ctrl+b n
ctrl+b [ : 页面滚动
任务提交dgx-dev : qsub -I -q dgx-dev

6.13

cmake添加前缀路径:CMAKE_PREFIX_PATH = /opt/fftw:/opt/cuda cmake ..

6.21

ssh -L [localport]:[host]:[hostport] [host] 将本地端口localport ssh forward到host的hostport上
ssh -D [localport] [host] 将host转发到localport

/sys : 在内存中
/var : variable,可能grow in size的内容

6.22

CPU flops计算:
对于nico的Intel® Xeon® Gold 5218 Processor:
每个核有1个AVX-512 FMA unit (单个时钟周期内两次512bit 的乘法和加法)
睿频3.9GHz,32位GFLOPS = 64 * 512 / 32 * 2 * 3.9 = 7987 => 8TFLOPS
单V100 single precision 14TFLOPS
带宽900GB/sec

nico1上v100的capability 7.0
参考一些 《CUDA C权威编程指南》里面的内容

atomicCAS(int *address, int compare, int val)
(*address == compare ? val : *address)
可以通过它对 capability < 6.0 设备实现 atomicAdd

一个有趣的东西 warp vote function:__all_sync/__any_sync...(unsigned mask, int predicate)
对wrap中 mask中每个线程传入一个predicate与0比较,返回结果到每个wrap

nvidia-smi -L : 查看所有GPU的设备ID
nvidia-smi -q -i 0 : 查看GPU0详细信息

一个block只能在一个SM上被调度
major revision number对应架构: Volta 7, Pascal 6, Maxwell 5, Kepler 3, Fermi 2, Tesla 1
Turing 7.5 (基于Volta的incremental)

-arch=compute_30 : 编译选项中使用3.0 capability

6.23

Fermi架构中shared memory 和 L1 cache 共享
Kepler : 每个SM中包含192个单精单元,64双精单元,32特殊单元,32加载存储单元
每个SM中有4个 wrap 调度器,8个指令调度器。可同时调度64个wrap,2048个线程
双精1TFLOPS
动态并行,GPU可以启动嵌套kernel

6.24

V100 release时间是2017.5
我觉得目前可以做的是研究V100的tensor core
PTX = parallel thread execution
active threads : 在所在wrap当前执行路径上的threads

ASpT的代码中大量用到了__shfl原语
在一个wrap的threads之间交换一个variable

int __shfl(int var, int srcLane, int width=warpSize);

返回srcLane中var的值
width必须是power of 2
width<warpSize时每width个thread被视为一个子wrap

matrix multiply and accumulate (D = A*B + C)

6.25

register bank conflict : volta架构中寄存器被分为两个64bit bank
一个volta指令每个cycle只能访问每个bank的64 bit

x86_64中,依次使用rdi, rsi, rdx传递函数参数
A100 GPU : 54 billion 晶体管 826 mm^2
ampere架构

6.26

intel cascade lake : 基于 sky lake 优化

为了push自己开了这个坑
主要记录一周里面每天在 实验室 和 超算 那边干的事情
以及大作业等方面的见闻
避免划水

5.5

尝试去装超算的Gromacs
看起来是一个分子模拟的应用
需要用到 intel 的 MKL 库
尝试去官网里面下,由于需要登录信息,因此wget的时候需要指定cookie wget --load-cooikes=cookies.txt [site]
安装时运行install.sh,自选了不安装Fortan部分
upd : 这个在编译的时候报了迷之错误,可能是MKL库没有链接好的问题,之后去问下学长吧

5.6

今天应该以TVM为主
首先在看TVM的developer guide,对TVM的语法结构更深地认识一下。
compiler stack中所有language object都是Object的子类
每个object有一个string type_key表示object的type
ObjectRef 相当于Object的sharedptr
每个Object的子类需要包含VisitAttr函数,通过重载VisitAttr访问成员
跑docker需要先加到docker组里面

和适之学长讨论了用TVM实现aspt的问题
目前的问题在于即使实现了thread间通过atomic_add的reduce功能,由于每个行块中列块数量不同,因此无法直接将列块数量视为一个额外的维度处理进行reduce
目前的方法是对每个行块中的所有列块用一个kernel进行reduce,但这种实现方法要求这些kernel之间并行执行
为此需要使用不同的stream执行这些kernel
那么现在的问题就是研究tvm使用stream的机制

TVM计算图中的node是placeholder或computational node
每个node包含:op(operation type),name等

TVM基于dmlc/HalideIR
HalideIR中loop_type(a,b)表示区间[a,a+b)
TVM中loop_type(a,b)表示区间[a,b)
HalideIR中的四种loop形式:
* A for loop. Execute the 'body' statement for all values of the * variable loop_var from 'min' to 'min + extent'. There are four * types of For nodes. A 'Serial' for loop is a conventional * one. In a 'Parallel' for loop, each iteration of the loop * happens in parallel or in some unspecified order. In a * 'Vectorized' for loop, each iteration maps to one SIMD lane, * and the whole loop is executed in one shot. For this case, * 'extent' must be some small integer constant (probably 4, 8, or * 16). An 'Unrolled' for loop compiles to a completely unrolled * version of the loop. Each iteration becomes its own * statement. Again in this case, 'extent' should be a small * integer constant.

5.7

开始调研搜索引擎大作业的技术栈
看bert-as-service的时候发现pretrained BERT Chinese是character-based。
目前的初步想法:从训练集中提取关键词,所有处理都基于这些关键词

5.8

白天在搞搜索引擎大作业
目前实现了从xml的value里面提取关键词,构造出一个jieba的关键词表。
发现正则处理简单xml的效果还不错
关键词主要有value里面出现过的,以及《》中的法律条文
然后用jieba对QW(全文)部分进行分词
下一步是通过word2vec构建词向量
晚上的时候主要focus在gormacs的编译上
折腾了好久,甚至在尝试把编译栈转移到icc上面,但后来发现他还依赖cuda,也就是说转移的话需要用icc编译的cuda
编译了一个icc的openmpi,icc编译的时候很慢,但编译后文件体积不大
最后和家傲学长的编译命令对比才发现,我看文档的时候不够仔细,文档的说明里面已经写了load mkl或者 加一个mkl的选项,但我load之后又加了选项,导致gg
目前为止的结论是cmake多生成的都会堆到当前文件夹下
所以把build删了就可以重新编译

5.9

在使用word2vec训练词向量
默认的epoch是5个

gromacs文件:
.pdb : protein databank file 描述分子结构
.top : topology 定义分子 包含nonbonded parameters (atom types and charges)和bonded parameters (bonds, angles, and dihedrals),topol.top : system topology
.gro : GROMACS-formatted structure file ,force field中的所有原子

5.10

早上起来继续看gromacs
.mdp : molecular dynamics parameter
.tpr : 系统中所有原子的所有参数,可通过.mdp生成

尝试跑了一下lignocellulose-rf.tpr的测例来测试性能
发现gromacs有一个GMX_THREAD_MPI的选项,不支持多机,但据说可以比MPI快一点
实测并没有太大用处
mpi版本命令:mpirun -n 4 gmx_mpi mdrun -s lignocellulose-rf.tpr -v -deffnm temp -ntomp 4 -nsteps 1000
threadMPI版本命令:gmx mdrun -s lignocellulose-rf.tpr -v -deffnm temp -ntomp 4 -ntmpi 4 -nsteps 1000

发现当前的编译选项用的是FFTW的数学库
目前感觉如果换一个intel-mkl的话可能可以获得性能提升
但现在mkl找library的时候还会遇到之前的问题
家傲学长说cmake有一个findmkl的子模块,之后去了解一下

[CLS]:在做分類任務時其最後一層的 repr. 會被視為整個輸入序列的 repr.
[SEP]:有兩個句子的文本會被串接成一個輸入序列,並在兩句之間插入這個 token 以做區隔
[UNK]:沒出現在 BERT 字典裡頭的字會被這個 token 取代
[PAD]:zero padding 遮罩,將長度不一的輸入序列補齊方便做 batch 運算
[MASK]:未知遮罩,僅在預訓練階段會用到

sequence : 一个或两个sentence

pretraining task:
masked language modeling(MLM) : 训练挖空填词
next-sentence prediction(NSP) : 训练判断一句话是否是另一句话的下文
albert 指出 NSP 的结果 unreliable,使用 sentence-order prediction(SOP) 替代 NSP
SOP 将一段打乱的话排序

BERT base : L(层数) 12, H(hidden size) 768, A(attention heads) 12, 总参数量:110M
BERT large : L 24, H 1024, A 16, 总参数量:340M

在 BERT 中 embedding size E 和 hidden size H 始终相等,embedding 层参数数为 V(vocabulary size) * H
在 ALBERT 中将参数数量 reduce 至 V * E + E * H

torch.cuda.is_available() #cuda是否可用
import torch
import torch.nn as nn

class MyModule(nn.Module):
    def __init__(self, <args>):
        super().__init__()
        #初始化
        self.fc = nn.Linear(in, out) 
        self.fc.weight.data.uniform_(-0.5, 0.5)
        self.fc.bias.data.zero_()

    def forward(self, <args>):
        return self.fc()


device = torch.device("cuda")

model = MyModule(<args>).to(device)
criterion = torch.nn.CrossEntropyLoss().to(device)
optimizer = torch.optim.SGD(model.parameters(), lr=4.0)
scheduler = torch.optim.lr_scheduler.StepLR(optimizer, 1, gamma=0.9)

def train(nn, iterator, optimizer, criteon):
    nn.train()
    for i, batch in enumerate(iterator):
        optimizer.zero_grad()
        pred = nn(batch.text)
        loss = criteon(pred, batch.label)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

def eval(nn, iterator, criteon):
    rnn.eval()
    with torch.no_grad():
        for batch in iterator:
            pred = nn(batch.text)
            loss = criteon(pred, batch.label)


变换

torch.triu(tensor) 将 tensor 变为右上三角
tensor.masked_fill(mask, value) 将 mask 为 True 位置用 value 填充
下标索引x::y:从x开始间隔y取一个

操作

model.parameters() : 返回模型所有参数的generator
tensor.numel() :返回模型参数数量

optim

torch.optim.SGD(model.parameters(), lr = 0.01, momentum)

layer

nn.Embedding(vocab_size, embedding_dim)
单词到word vector
vocab_size : 词汇量大小
输入:1维index索引
输出:embedding_dim维word vector

nn.LSTM(embedding_dim, hidden_dim, num_layers = 2)
LSTM层
dropout:默认0
bidirectional:默认False

nn.Linear(in_size, out_size)
全连接层

torch.cat([hidden[-2], hidden[-1]], )

并行

if local_rank != 0:
    torch.distributed.barrier()
# 只有主进程执行
if local_rank == 0:
    torch.distributed.barrier()
#所有进程执行

model = torch.nn.DataParallel(model) #多GPU数据并行
torch.cuda.device_count() #GPU数量
model = torch.nn.DataParallel(model) #多GPU数据并行