跳转至

2022

Vtune Assembly Analysis

超算机器用vtune的命令行文件分析

  1. 首先找到vtune程序
> module load intel/2022.1                                    
> which icc                                                        
/public1/soft/oneAPI/2022.1/compiler/latest/linux/bin/intel64/icc                          
> cd /public1/soft/oneAPI/2022.1  
> find . -executable -type f -name "*vtune*"
./vtune/2022.0.0/bin64/vtune-worker-crash-reporter
./vtune/2022.0.0/bin64/vtune-gui.desktop
./vtune/2022.0.0/bin64/vtune-gui
./vtune/2022.0.0/bin64/vtune-agent
./vtune/2022.0.0/bin64/vtune-self-checker.sh
./vtune/2022.0.0/bin64/vtune-backend
./vtune/2022.0.0/bin64/vtune-worker
./vtune/2022.0.0/bin64/vtune
./vtune/2022.0.0/bin64/vtune-set-perf-caps.sh
  1. vtune-gui获取可执行命令
/opt/intel/oneapi/vtune/2021.1.1/bin64/vtune -collect hotspots -knob enable-stack-collection=true -knob stack-size=4096 -data-limit=1024000 -app-working-dir /home/shaojiemike/github/IPCC2022first/build/bin -- /home/shaojiemike/github/IPCC2022first/build/bin/pivot /home/shaojiemike/github/IPCC2022first/src/uniformvector-2dim-5h.txt
  1. 编写sbatch_vtune.sh
#!/bin/bash
#SBATCH -o ./slurmlog/job_%j_rank%t_%N_%n.out
#SBATCH -p IPCC
#SBATCH -t 15:00
#SBATCH --nodes=1
#SBATCH --exclude=
#SBATCH --cpus-per-task=64
#SBATCH --mail-type=FAIL
#SBATCH [email protected]

source /public1/soft/modules/module.sh
module purge

module load intel/2022.1

logname=vtune
export OMP_PROC_BIND=close; export OMP_PLACES=cores
# ./pivot |tee ./log/$logname
/public1/soft/oneAPI/2022.1/vtune/2022.0.0/bin64/vtune -collect hotspots -knob enable-stack-collection=true -knob stack-size=4096 -data-limit=1024000 -app-working-dir /public1/home/ipcc22_0029/shaojiemike/slurm -- /public1/home/ipcc22_0029/shaojiemike/slurm/pivot /public1/home/ipcc22_0029/shaojiemike/slurm/uniformvector-2dim-5h.txt |tee ./log/$logname
  1. log文件如下,但是将生成的trace文件r000hs导入识别不了AMD

> cat log/vtune
dim = 2, n = 500, k = 2
Using time : 452.232000 ms
max : 143 351 58880.823709
min : 83 226 21884.924801
Elapsed Time: 0.486s
   CPU Time: 3.540s
      Effective Time: 3.540s
      Spin Time: 0s
      Overhead Time: 0s
   Total Thread Count: 8
   Paused Time: 0s

Top Hotspots
Function         Module  CPU Time  % of CPU Time(%)
---------------  ------  --------  ----------------
SumDistance      pivot     0.940s             26.6%
_mm256_add_pd    pivot     0.540s             15.3%
_mm256_and_pd    pivot     0.320s              9.0%
_mm256_loadu_pd  pivot     0.300s              8.5%
Combination      pivot     0.250s              7.1%
[Others]         N/A       1.190s             33.6%

汇编

objdump -Sd ../build/bin/pivot > pivot1.s
gcc -S -O3 -fverbose-asm ../src/pivot.c -o pivot_O1.s

汇编分析技巧

https://blog.csdn.net/thisinnocence/article/details/80767776

如何设置GNU和Intel汇编语法

vtune汇编实例

(没有开O3,默认值)

偏移 -64 是k

-50 是ki

CDQE复制EAX寄存器双字的符号位(bit 31)到RAX的高32位。

这里的movsdq的q在intel里的64位,相当于使用了128位的寄存器,做了64位的事情,并没有自动向量化。

生成带代码注释的O3汇编代码

如果想把 C 语言变量的名称作为汇编语言语句中的注释,可以加上 -fverbose-asm 选项:

gcc -S -O3 -fverbose-asm ../src/pivot.c -o pivot_O1.s
.L15:
# ../src/pivot.c:38:                 double dis = fabs(rebuiltCoordFirst - rebuiltCoordSecond);
   movsd (%rax), %xmm0 # MEM[base: _15, offset: 0B], MEM[base: _15, offset: 0B]
   subsd (%rax,%rdx,8), %xmm0 # MEM[base: _15, index: _21, step: 8, offset: 0B], tmp226
   addq $8, %rax #, ivtmp.66
# ../src/pivot.c:38:                 double dis = fabs(rebuiltCoordFirst - rebuiltCoordSecond);
   andpd %xmm2, %xmm0 # tmp235, dis
   maxsd %xmm1, %xmm0 # chebyshev, dis
   movapd %xmm0, %xmm1 # dis, chebyshev
# ../src/pivot.c:35:             for(ki=0; ki<k; ki++){
   cmpq %rax, %rcx # ivtmp.66, _115
   jne .L15 #,
.L19:
# ../src/pivot.c:32:         for(j=i+1; j<n; j++){
   addl $1, %esi #, j
# ../src/pivot.c:41:             chebyshevSum += chebyshev;
   addsd %xmm1, %xmm4 # chebyshev, <retval>
   addl %r14d, %edi # k, ivtmp.75
# ../src/pivot.c:32:         for(j=i+1; j<n; j++){
   cmpl %esi, %r15d # j, n
   jg .L13 #,
# ../src/pivot.c:32:         for(j=i+1; j<n; j++){
   addl $1, %r10d #, j
# ../src/pivot.c:32:         for(j=i+1; j<n; j++){
   cmpl %r10d, %r15d # j, n
   jne .L16 #,

vtune O3汇编分析

原本以为O3是看不了原代码与汇编的对应关系的,但实际可以-g -O3 是不冲突的。

指令的精简合并

  1. 访存指令的合并
  2. r9 mov到 rax里,
    1. leaq (%r12,%r8,8), %r9。其中r12rebuiltCoord,所以r8原本存储的是[i*k]的值
    2. raxrebuiltCoord+[i*k]的地址,由于和i有关,index的计算在外层就计算好了。
  3. rdx的值减去r8存储在rdx
    1. rdx原本存储的是[j*k]的地址
    2. r8原本存储的是[i*k]的值
    3. rdx之后存储的是[(j-i)*k]的地址
  4. data16 nop是为了对齐插入的nop
  5. 值得注意的是取最大值操作,这里变成了maxsd
  6. xmm0缓存值
  7. xmm1chebyshev
  8. xmm2fabs的掩码
  9. xmm4chebyshevSum

自动循环展开形成流水

  1. r14d存储k的值,所以edi存储j*k
  2. Block22后的指令验证了rdx原本存储的是[j*k]的地址
  3. 最外层循环
  4. 因为r14d存储k的值,r8r11d存储了i*k的值

从汇编看不出有该操作,需要开启编译选项

自动向量化

从汇编看不出有该操作,需要开启编译选项

自动数据预取

从汇编看不出有该操作,需要开启编译选项

问题

为什么求和耗时这么多

添加向量化选项

gcc

Baseline

-mavx2 -march=core-avx2

  1. 阅读文档, 虽然全部变成了vmov,vadd的操作,但是实际还是64位的工作。
  2. 这点add rax, 0x8没有变成add rax, 0x16可以体现
  3. 但是avx2不是256位的向量化吗?用的还是xmm0这类的寄存器。
VADDSD (VEX.128 encoded version)
DEST[63:0] := SRC1[63:0] + SRC2[63:0]
DEST[127:64] := SRC1[127:64]
DEST[MAXVL-1:128] := 0

ADDSD (128-bit Legacy SSE version)
DEST[63:0] := DEST[63:0] + SRC[63:0]
DEST[MAXVL-1:64] (Unmodified)

-march=skylake-avx512

汇编代码表面没变,但是快了10s(49s - 39s)

下图是avx2的 下图是avx512的

猜测注意原因是

  1. nop指令导致代码没对齐
  2. 不太可能和红框里的代码顺序有关

添加数据预取选项

判断机器是否支持

lscpu|grep pref
3dnowprefetch //3DNow prefetch instructions

应该是支持的

汇编分析

虽然时间基本没变,主要是对主体循环没有进行预取操作,对其余循环(热点占比少的)有重新调整。如下图增加了预取指令

添加循环展开选项

变慢很多(39s -> 55s)

-funroll-loops

汇编实现,在最内层循环根据k的值直接跳转到对应的展开块,这里k是2。 默认是展开了8层,这应该和xmm寄存器总数有关

分析原因

  1. 循环展开的核心是形成计算和访存的流水
  2. 不是简单的少几个跳转指令
  3. 这种简单堆叠循环核心的循环展开,并不能形成流水。所以时间不会减少
  4. 但是完全无法解释循环控制的时间增加
  5. 比如图中cmp的次数应该减半了,时间反而翻倍了

手动分块

由于数据L1能全部存储下,没有提升

手动数据预取

并没有形成想象中预取的流水。每512位取,还有重复。

每次预取一个Cache Line,后面两条指令预取的数据还有重复部分(导致时间增加 39s->61s)

想预取全部,循环每次预取了512位=64字节

手动向量化

avx2

(能便于编译器自动展开来使用所有的向量寄存器,avx2

39s -> 10s -> 8.4s 编译器

for(i=0; i<n-blockSize; i+=blockSize){
   for(j=i+blockSize; j<n-blockSize; j+=blockSize){
      for(ii=i; ii<i+blockSize; ii++){
            __m256d vi1 = _mm256_broadcast_sd(&rebuiltCoord[0*n+ii]);
            __m256d vi2 = _mm256_broadcast_sd(&rebuiltCoord[1*n+ii]);

            __m256d vj11 = _mm256_loadu_pd(&rebuiltCoord[0*n+j]); //读取4个点
            __m256d vj12 = _mm256_loadu_pd(&rebuiltCoord[1*n+j]);

            __m256d vj21 = _mm256_loadu_pd(&rebuiltCoord[0*n+j+4]); //读取4个点
            __m256d vj22 = _mm256_loadu_pd(&rebuiltCoord[1*n+j+4]);

            vj11 = _mm256_and_pd(_mm256_sub_pd(vi1,vj11), vDP_SIGN_Mask);
            vj12 = _mm256_and_pd(_mm256_sub_pd(vi2,vj12), vDP_SIGN_Mask);

            vj21 = _mm256_and_pd(_mm256_sub_pd(vi1,vj21), vDP_SIGN_Mask);
            vj22 = _mm256_and_pd(_mm256_sub_pd(vi2,vj22), vDP_SIGN_Mask);

            __m256d tmp = _mm256_add_pd(_mm256_max_pd(vj11,vj12), _mm256_max_pd(vj21,vj22));
            _mm256_storeu_pd(vchebyshev1, tmp);

            chebyshevSum += vchebyshev1[0] + vchebyshev1[1] + vchebyshev1[2] + vchebyshev1[3];

            // for(jj=j; jj<j+blockSize; jj++){
            //     double chebyshev = 0;
            //     int ki;
            //     for(ki=0; ki<k; ki++){
            //         double dis = fabs(rebuiltCoord[ki*n + ii] - rebuiltCoord[ki*n + jj]);
            //         chebyshev = dis>chebyshev ? dis : chebyshev;
            //     }
            //     chebyshevSum += chebyshev;
            // }
      }
   }
}

明明展开了一次,但是编译器继续展开了,总共8次。用满了YMM 16个向量寄存器。

下图是avx512,都出现寄存器ymm26了。

vhaddpd是水平的向量内加法指令

avx512

当在avx512的情况下展开4次,形成了相当工整的代码。

  1. 向量用到了寄存器ymm18,估计只能展开到6次了。
  2. avx2 应该寄存器不够

最后求和的处理,编译器首先识别出了,不需要实际store。还是在寄存器层面完成了计算。并且通过三次add和两次数据 移动指令自动实现了二叉树型求和。

avx2 寄存器不够会出现下面的情况。

avx求和的更快速归约

假如硬件存在四个一起归约的就好了,但是对于底层元件可能过于复杂了。

__m256d _mm256_hadd_pd (__m256d a, __m256d b);
VEXTRACTF128 __m128d _mm256_extractf128_pd (__m256d a, int offset);

如果可以实现会节约一次数据移动和一次数据add。没有分析两种情况的寄存器依赖。可能依赖长度是一样的,导致优化后时间反而增加一点。

对于int还有这种实现

将横向归约全部提取到外面

并且将j的循环展开变成i的循环展开

手动向量化+手动循环展开?

支持的理由:打破了循环间的壁垒,编译器会识别出无效中间变量,在for的jump指令划出的基本块内指令会乱序执行,并通过寄存器重命名来形成最密集的计算访存流水。

不支持的理由:如果编译器为了形成某一指令的流水,占用了太多资源。导致需要缓存其他结果(比如,向量寄存器不够,反而需要额外的指令来写回,和产生延迟。

理想的平衡: 在不会达到资源瓶颈的情况下展开。

支持的分析例子

手动展开后,识别出来了连续的访存应该在一起进行,并自动调度。将+1的偏移编译器提前计算了。

如果写成macro define,可以发现编译器自动重排了汇编。

不支持的分析例子

avx2可以看出有写回的操作,把值从内存读出来压入栈中。

寄存器足够时没有这种问题

寻找理想的展开次数

由于不同代码对向量寄存器的使用次数不同,不同机器的向量寄存器个数和其他资源数不同。汇编也难以分析。在写好单次循环之后,最佳的展开次数需要手动测量。如下图,6次应该是在不会达到资源瓶颈的情况下展开来获得最大流水。

for(j=beginJ; j<n-jBlockSize; j+=jBlockSize){  /
//展开jBlockSize次
}
for(jj=j; jj<n; jj++){  //j初始值继承自上面的循环
//正常单次
}

由于基本块内乱序执行,代码的顺序也不重要。 加上寄存器重命名来形成流水的存在,寄存器名也不重要。当然数据依赖还是要正确。

对于两层循环的双层手动展开

思路: 外层多load数据到寄存器,但是运行的任何时候也不要超过寄存器数量的上限(特别注意在内层循环运行一遍到末尾时)。 左图外层load了8个寄存器,但是右边只有2个。

特别注意在内层循环运行一遍到末尾时: 如图,黄框就有16个了。

注意load的速度也有区别

所以内层调用次数多,尽量用快的

_mm256_loadu_ps >> _mm256_broadcast_ss > _mm256_set_epi16
0.04 >> 0.5
vsub  vmax    ps 0.02      Latency 4
vand                       Latency 1

vadd              ps 0.80              Throughput 0.5
vhadd                      Latency 7
vcvtps2pd            2.00  Latency 7
vextractf128         0.50  Latency 3

|指令|精度|时间(吞吐延迟和实际依赖导致)|Latency|Throughput |-|-|-|-|-|-| |_mm256_loadu_ps /_mm256_broadcast_ss|||7|0.5 |vsub vmax | ps| 0.02 | 4|0.5 vand ||0.02| 1|0.33 vadd |ps |0.80 |4| 0.5 vhadd ||0.8| 7|2 vcvtps2pd || 2.00 | 7|1 vextractf128 || 0.50 | 3|1

向量化double变单精度没有提升

17条avx计算 5load 2cvt 2extract

单位时间 | avx计算|load|cvt |extract |-|-|-|-|-| ||2.33|3.68|12.875|4.1|

可见类型转换相当耗费时间,最好在循环外,精度不够,每几次循环做一次转换。

GCC编译器优化

-march=skylake-avx512是一条指令

-mavx2 是两条指令

vmovupd xmm7, xmmword ptr [rdx+rsi*8]
vinsertf128 ymm1, ymm7, xmmword ptr [rdx+rsi*8+0x10], 0x1

原因是不对齐的访存在老架构上可能更快

O3对于核心已经向量化的代码还有加速吗?

将IPCC初赛的代码去掉O3发现还是慢了10倍。

为什么连汇编函数调用也慢这么多呢?

这个不开O3的编译器所属有点弱智了,一条指令的两个操作数竟然在rbp的栈里存来存去的。

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

HPCAI

HPC - AI 在第一性原理分子动力学中的应用(中科院计算所)10^10规模

一块铁 10^22个原子

高性能计算每年翻一倍 = 超算规模 + Chip摩尔定律

但是由于分子动力学的方法是O^3, 问题规模增大,每一步迭代反而变慢了(2011年GB是3天一步)。

一百万内规模6次方内的专用机器 anton? ,比一般超算快100倍。 1. 通讯精度压缩, 2. 专用网络和通讯协议设计。

compute is cheap,memory and bandwidth are expansive, latency is physics.

18GB: AI图片处理大气模拟问题

AI : 高纬度函数的逼近(解空间相对于输入维度)

  1. 通过物理信息如何设计网络,来避免local minimal
  2. 其余技巧
  3. 10步AI,一步DFT
    1. 大哈密顿量矩阵的切片法,融合在纯粹的数据AI里。
  4. 预测误差

  1. 低精度相乘法,高精度相加
  2. 单精度相对于双精度
  3. 速度提升可能没有2倍
  4. 但是内存需求变成一半了,规模可以两倍

将epoch从几百变几个

字节量子计算机上的量子化学模拟 Dingshun Li

基于薛定谔方程和经典电子结构

digist + analog

量子计算的a killer app

当前问题: 1. 量子规模 50~100 2. 量子计算机运行时间有限 3. 纠错机制还需要相位纠错 4. 由于叠加态连续性,导致的误差

量子计算缺乏复杂度分析?

UCC Ansatz

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

Module Command

基本使用

命令 作用
module avail 或 module av 查看系统中可用的软件
module add 或 module load 加载模块
module rm 或 unload 卸载模块
module list 或 module li 显示已加载模块
module purge 卸载所有模块
module show 显示模块配置文件
module swap 或 module switch 将模块1 替换为 模块2
module help 查看具体软件的信息
source /public1/soft/modules/module.sh 
source /public1/soft/modules/init/zsh 

$ cat module.sh 
#!/bin/sh
source /public1/soft/modules/init/bash
export MODULEPATH=/public1/soft/modulefiles

$ module list
No Modulefiles Currently Loaded.

$ module show intel/2022.1
-------------------------------------------------------------------
/public1/soft/modulefiles/intel/2022.1:

module-whatis   loads the environment of intel oneAPI 2022.1
unsetenv        MKLROOT
setenv          MKLROOT /public1/soft/oneAPI/2022.1/mkl/latest
prepend-path    MANPATH /public1/soft/oneAPI/2022.1/inspector/latest/man
unsetenv        INTEL_LICENSE_FILE
prepend-path    LIBRARY_PATH /public1/soft/oneAPI/2022.1/ipp/latest/lib
prepend-path    LD_LIBRARY_PATH /public1/soft/oneAPI/2022.1/ipp/latest/lib
prepend-path    CPATH /public1/soft/oneAPI/2022.1/ipp/latest/include
prepend-path    NLSPATH /public1/soft/oneAPI/2022.1/mkl/latest/lib/intel64/locale
prepend-path    PATH /public1/soft/oneAPI/2022.1/compiler/latest/linux/bin
setenv          TBBROOT /public1/soft/oneAPI/2022.1/tbb/latest
setenv          DAALROOT        /public1/soft/oneAPI/2022.1/dal/latest

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

SlurmCommand

PBS vs SLURM

更详细在这里

查看仍在运行作业7454119的详细信息

scontrol show job 7454119
squeue -u USERNAME #来查看目前处于运行中的作业。

sacct查询已经结束作业的相关信息

format=jobid,jobname,partition,nodelist,alloccpus,state,end,start,submit
sacct --format=$format -j 7454119
[sca3190@ln121%bscc-a5 ~]$ sacct -D -T -X -u sca3190 -S 2021-11-10T00:00:00 -E 2021-11-30T00:00:00 --format "JobID,User,JobName,Partition,QOS,Elapsed,Start,NodeList,State,ExitCode,workdir%70"
JobID             User    JobName  Partition        QOS    Elapsed               Start        NodeList      State ExitCode                                                                WorkDir 
------------ --------- ---------- ---------- ---------- ---------- ------------------- --------------- ---------- -------- ---------------------------------------------------------------------- 
1050223        sca3190       LQCD    amd_256     normal 19-23:25:24 2021-11-10T00:34:36   fa[0208,0211]  NODE_FAIL      0:0                             /public1/home/sca3190/VEC_REORDER_LQCD/src 

[sca3190@ln121%bscc-a5 ~]$ sacct -D -T -X -u sca3190 -S 2021-11-10T00:00:00 -E 2021-11-30T00:00:00 --format "JobID,User,JobName,Partition,QOS,Elapsed,Start,NodeList,State,ExitCode,workdir%70,Timelimit,Submitline%20,Submit,Layout"
JobID             User    JobName  Partition        QOS    Elapsed               Start        NodeList      State ExitCode                                                                WorkDir  Timelimit           SubmitLine              Submit    Layout 
------------ --------- ---------- ---------- ---------- ---------- ------------------- --------------- ---------- -------- ---------------------------------------------------------------------- ---------- -------------------- ------------------- --------- 
1050223        sca3190       LQCD    amd_256     normal 19-23:25:24 2021-11-10T00:34:36   fa[0208,0211]  NODE_FAIL      0:0                             /public1/home/sca3190/VEC_REORDER_LQCD/src  UNLIMITED                      2021-11-10T00:11:29           

OpenMP申请

1个task 64核

#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --cpus-per-task=64

问题

IPCC比赛耗时特别多

建议sbatch 加入-t, --time=minutes time limit #SBATCH -t 5:00

第二年参加IPCC发现去年的一个程序跑了很久。

导出excel 获得jobID 1050223

$ sacct -D -T -X -u sca3190 -S 2021-11-10T00:00:00 -E 2021-11-30T00:00:00 --format "JobID,JobName,State,workdir%70"
JobID           JobName      State                                                                WorkDir 
------------ ---------- ---------- ---------------------------------------------------------------------- 
1050223            LQCD  NODE_FAIL                             /public1/home/sca3190/VEC_REORDER_LQCD/src 

NODE_FAIL - Job terminated due to failure of one or more allocated nodes.

查看提交脚本,没有什么问题。

#!/bin/bash
#SBATCH -o ./slurmlog/job_%j_rank%t_%N_%n.out
#SBATCH -p amd_256
#SBATCH -J LQCD
#SBATCH --nodes=2
#SBATCH --ntasks-per-node=64
#SBATCH --exclude=
#SBATCH --cpus-per-task=1
#SBATCH --mail-type=FAIL
#SBATCH [email protected]

source /public1/soft/modules/module.sh
module purge
CC=mpiicc
CXX=mpiicpc
CXX_FLAGS=""
raw_flags="-fPIC -I../include  -std=c++11 -march=core-avx2"

MPIOPT=
computetimes="ibug_buffer"
taskname=so_${CC}_${CXX}_${CXX_FLAGS}

export OMP_NUM_THREADS=$SLURM_CPUS_PER_TASK
module load intel/20.4.3
module load mpi/intel/20.4.3

make clean
make CC=$CC CXX=$CXX CXX_FLAGS="${CXX_FLAGS}${raw_flags}" TARGET=$taskname
mpirun ./$taskname 0.005  ../data/ipcc_gauge_48_96  48 48 48 96  12 24 24 12 > ./log/3_$taskname$computetimes.log
查看Log文件
sca3190@ln121%bscc-a5 src]$ cat slurmlog/job_1050223_rank0_fa0208_0.out 
rm -rf liblattice.so dslash.o lattice_fermion.o lattice_gauge.o invert.o  check.o load_gauge.o main
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o dslash.o -c dslash.cpp
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o lattice_fermion.o -c lattice_fermion.cpp
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o lattice_gauge.o -c lattice_gauge.cpp
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o invert.o -c invert.cpp
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o check.o -c check.cpp
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -o load_gauge.o -c load_gauge.cpp
mpiicpc --shared dslash.o lattice_fermion.o lattice_gauge.o invert.o check.o load_gauge.o -o liblattice.so 
mpiicpc -fPIC -I../include  -std=c++11 -march=core-avx2 -Wl,-rpath=./ -lmpi -o so_mpiicc_mpiicpc_  main.cpp -L./ -llattice
slurmstepd: error: *** JOB 1050223 ON fa0208 CANCELLED AT 2022-04-20T14:45:43 DUE TO NODE FAILURE, SEE SLURMCTLD LOG FOR DETAILS ***
[[email protected]] check_exit_codes (../../../../../src/pm/i_hydra/libhydra/demux/hydra_demux_poll.c:121): unable to run bstrap_proxy (pid 59376, exit code 256)
[[email protected]] poll_for_event (../../../../../src/pm/i_hydra/libhydra/demux/hydra_demux_poll.c:159): check exit codes error
[[email protected]] HYD_dmx_poll_wait_for_proxy_event (../../../../../src/pm/i_hydra/libhydra/demux/hydra_demux_poll.c:212): poll for event error
[[email protected]] HYD_bstrap_setup (../../../../../src/pm/i_hydra/libhydra/bstrap/src/intel/i_hydra_bstrap.c:772): error waiting for event
[[email protected]] main (../../../../../src/pm/i_hydra/mpiexec/mpiexec.c:1938): error setting up the boostrap proxies
猜测原因是: 卡在编译了。

以后最好不要在sbatch脚本里编译

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

Processing In/near Memory

缘由

  1. 指令为中心,数据移动带来的功耗墙,性能墙
  2. 内存计算的经典模式
  3. 3D的内存技术
    1. Through silicon vias
  4. ReRAM 新型结构

PIM分类

  1. 按照PIM core和memory的距离分类

  2. 新的内存工艺使得内存的最小电路单元具有计算能力(忆阻器)

  3. 基于现有的商业DRAM和处理器的设计(加速的上限低一些,但是落地推广应用的阻力也越小, 应用范围更广,编程困难低)
  4. 基于3D堆叠memory(HMC)的设计(Starting from HMC 2.0, it supports the execution of 18 atomic operations in its logic layer.)
  5. 在每个最小存储单元融入计算能力(可以结合忆阻器)
  6. 完整的处理器核,有cache hierarchy
  7. 简单一点的应用相关的硬件计算单元
  8. 或者更简单的Functional Units (FUs)

关键技术

  1. 传统器件
  2. 地址翻译
    1. 三种不同解决思路
      1. 全部由CPU负责指令的发射和翻译
      2. 使能PIM侧页表管理,翻译机制
      3. 物理地址空间隔离(交互时需要拷贝),PIM独立管理地址空间
  3. 数据映射
    1. 物理内存地址排列的冲突(比如 GPUbank)
      1. CPU高带宽访存(会把数据分散来实现高带宽) vs PIM空间局部性(连续数据会跨多个颗粒)
    2. 纯软件方案或者软硬件结合大方案
  4. 安全性
    1. 物理内存被暴露在PIM core下,需要新的机制来确保内存安全。
  5. 数据一致性
    1. 现有一致性协议拓展差
    2. 核数量超级多,成千上万
    3. 解决方法
      1. 内存空间隔离,避免共享
      2. 弱化一致性问题,只处理特殊条件下一致性(eg.任务迁移)
      3. 批量处理一致性请求
  6. 新型器件
  7. 计算误差
  8. 外围电路大
  9. 异构编程模型
  10. 应用场景和编程模型
  11. 高能效比
  12. 高并行和NUMA访问
  13. 识别PIM函数的条件(什么函数适合用PIM做)
    1. 在所有函数中能耗最高
    2. 数据移动占据应用大比例,或者说是唯一的
    3. 访存密集型(通过LLC miss rate来判断)

根据PIM距离Memory的距离分成三类 1. NDP GPU 2. ? 3. ?

论文1

https://arxiv.org/pdf/2110.01709.pdf

论文2

hardware architecture and software stack for pim based on commercial dram technology

论文3

pim-enabled instructions a low-overhead locality-aware processing-in-memory architecture

论文4

展望

问题

  1. 由于核很小,不支持OS
  2. 但是可以支持message pass(reduce等)
  3. HPC应用经过数学变化后有些变成稀疏计算的,这时候变成memory-bound。所以PIM减少了数据移动,这时提升比较大。
  4. PIM的优势在于能效比,功耗的降低。而不是绝对性能。
  5. 单chip多核怎么通过PIM的思想,软件调度来实现?(不就是减少数据移动,和更近)

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

Microarchitecture: Micro-Fusion & Macro-Fusion

Micro-Fusion

历史原因

  1. 有很多对内存进行操作的指令都会被分成两个或以上的μops,如 add eax, [mem] 在解码时就会分成 mov tmp, [mem]; add eax, tmp。这类型的指令在前端只需要fetch与decode一条指令,相比原来的两条指令占用更少资源(带宽、解码资源、功耗),不过由于在解码后分成多个μops,占用资源(μop entries)增多,但是throughput相对较小,使得RAT以及RRF阶段显得更为拥堵
  2. 随着技术的发展,CPU内部指令处理单元(execution unit)以及端口(port)增多。相对,流水线中的瓶颈会出现在register renaming(RAT)以及retirement(RRF)

为了突破RAT以及RRF阶段的瓶颈,Intel从Pentium M处理器开始引入了micro-fusion技术。

解决办法

在RAT以及RRF阶段,把同一条指令的几个μops混合成一个复杂的μop,使得其只占用一项(比如在ROB里,但是Unlaminated μops会占用2 slots);

而在EU阶段,该复杂μop会被多次发送到EU中进行处理,表现得像是有多个已被分解的μops一样。(每个uops还是要各自运行)

可以micro-fused的指令

其中一条uops是load或者store

  1. 所有的store指令,写回内存的store指令分为两个步骤:store-address、store-data。
  2. 所有读内存与运算的混合指令(load+op),如:
    • ADDPS XMM9, OWORD PTR [RSP+40]
    • FADD DOUBLE PTR [RDI+RSI*8]
    • XOR RAX, QWORD PTR [RBP+32]
  3. 所有读内存与跳转的混合指令(load+jmp),如:
    • JMP [RDI+200]
    • RET
  4. CMP与TEST对比内存操作数并与立即数的指令(cmp mem-imm)。

例外的指令

不能采用RIP寄存器进行内存寻址:

CMP [RIP+400], 27
MOV [RIP+3000], 142
JMP [RIP+5000000]
采用了RIP寄存器进行内存寻址的指令是不能被micro-fused的,并且这些指令只能由decoder0进行解码。

Macro-Fusion

历史原因

为了占用更少的资源,Intel在酷睿处理器引入macro-fusion(Macro-Op Fusion, MOP Fusion or Macrofusion)

解决办法

在IQ时读取指令流,把两条指令组合成一个复杂的μop,并且在之后decode等流水线各个阶段都是认为是一项uops。

macro-fused后的指令可以被任意decoder进行解码

可以macro-fused的指令

其他架构ARM,RISC-V见wikiChip

Intel的要求如下: 1. 两条指令要相互紧邻 2. 如果第一条指令在缓存行的第63个字节处结束,而第二条指令在下一行的第0个字节处开始,则无法进行fusion。 3. 两条指令要满足下表,更新的架构可能会拓展 4.

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://www.cnblogs.com/TaigaCon/p/7702920.html

https://blog.csdn.net/hit_shaoqi/article/details/106630483

Microarchitecture: Out-Of-Order execution(OoOE/OOE) & Register Renaming

乱序执行的步骤

简单来说每个阶段执行的操作如下:1

1)获取指令,解码后存放到执行缓冲区Reservations Stations 2)乱序执行指令,结果保存在一个结果序列中 3)退休期Retired Circle,重新排列结果序列及安全检查(如地址访问的权限检查),提交结果到寄存器

  1. 取指令/uops
  2. 指令(uops)dispatch 到instruction queue (/instruction buffer / reservation stations).
  3. 指令等待操作数指令可用,然后可以在前后指令前离开等待队列
  4. issue到对应port单元执行,并且在 scheduler(reservation station)里跟踪uops依赖。
  5. 结果缓存在(re-order buffer, ROB)
  6. 在Tomasulo算法中,重排序缓冲区(英语:re-order buffer, ROB))可以使指令在乱序执行,之后按照原有顺序提交。
  7. 按照程序序结束(只有前面的指令都完成写回寄存器的操作),该指令才能retire
  8. 在retire的时候,重新排序运算结果来实现指令的顺序执行中的运行结果

why out-of-order execution retire/commit in program order

  1. 对于程序员外部视角来看,程序还是按序执行的。
  2. 如果指令出错,可以精确定位exceptions 位置,并且执行回滚来复原。
  3. ???寄存器数据依赖(重命名打破?)

乱序执行的实现

scoreboard

只有当一条指令与之前已发射(issue)的指令之间的冲突消失之后,这条指令才会被发射、执行。

如果某条指令由于数据冲突而停顿,计分板会监视正在执行的指令流,在所有数据相关性造成的冲突化解之后通知停顿的指令开始执行。

Tomasulo 托马苏洛算法

通过寄存器重命名机制,来解决后两种数据依赖。

使用了共享数据总线(common data bus, CDB)将已计算出的值广播给所有需要这个值作为指令源操作数的保留站

在指令的发射(issue)阶段,如果操作数和保留站都准备就绪,那么指令就可以直接发射并执行。

如果操作数未就绪,则进入保留站的指令会跟踪即将产生这个所需操作数的那个功能单元。

乱序执行的发展

随着流水线pipeline的加深和主存(或者缓存)和处理器间的速度差的变大。在顺序执行处理器等待数据的过程中,乱序执行处理器能够执行大量的指令。使得乱序执行更加重要。

Register Renaming

来由

已知可以通过乱序执行来实现,硬件资源的高效利用(避免计算指令等待访存指令的完成)。为了实现乱序执行,需要通过寄存器重命名来打破寄存器的之间的读写依赖。

例子1

对于原始代码

1. R1=M[1024]
2. R1=R1+2
3. M[1032]=R1
4. R1=M[2048]
5. R1=R1+4
6. M[2056]=R1

原本代码前后3条是没有关系的,可以并行的。需要使用寄存器重命名来解决R1的读后写依赖。

1. R1=M[1024] 4. R2=M[2048]
2. R1=R1+2     5. R2=R2+4
3. M[1032]=R1 6. M[2056]=R2

数据冲突

如果多条指令使用了同一个存储位置,这些指令如果不按程序地址顺序执行可能会导致3种数据冲突(data hazard):

  • 先写后Read-after-write,RAW):从寄存器或者内存中读取的数据,必然是之前的指令存入此处的。直接数据相关(true data dependency)

  • 先写后Write-after-write,WAW):连续写入特定的寄存器或内存,那么该存储位置最终只包含第二次写的数据。这可以取消或者废除第一次写入操作。WAW相关也被说成是“输出相关”(output dependencies)。

  • 先读后Write-after-read,WAR):读操作获得的数据是此前写入的,而不是此后写操作的结果。因此并行和乱序时无法改善的资源冲突(antidependency)。

后面两个WAW和WAR可以通过寄存器重命名解决(register renaming),不必等待前面的读写操作完成后再执行写操作,可以保持这个存储位置的两份副本:老值与新值。

前一条指令的读老值的操作可以继续进行,无需考虑那些后一条指令的写新值甚至该写新值指令之后的读新值的操作。产生了额外的乱序执行机会。当所有读老值操作被满足后,老值所使用的寄存器既可以释放。这是寄存器重命名的实质

重命名存储对象

任何被读或写的存储都是可以被重名。

  1. 最常考虑的是通用整数寄存器与浮点寄存器。
  2. 标志寄存器、状态寄存器甚至单个状态位也是常见的重命名的对象。
  3. 内存位置也可以被重命名,虽然这么做不太常见。

通用(逻辑)寄存器和物理寄存器

对于某种ISA,有固定的供编译器/汇编器访问使用的寄存器。例如,Alpha ISA使用32个64位宽整数寄存器,32个64位宽浮点寄存器。

但是一款特定的处理器,实现了这种处理器体系结构。例如Alpha 21264有80个整数寄存器、72个浮点寄存器,作为处理器内物理实现的寄存器。

寄存器个数设计考虑

如果寄存器个数很多,就不需要寄存器重命名机制。比如IA-64指令集体系结构提供了128个通用寄存器。但是这会导致一些问题:

  1. 编译器如果需要重用寄存器会很容易导致程序尺寸大增
  2. 程序的循环连续迭代执行就需要复制循环体的代码以使用不同的寄存器,这种技术叫做循环展开。
  3. 代码尺寸增加,会导致指令高速缓存的未命中(cache miss)增加,处理器执行停顿等待从低级存储中读入代码。这对运算性能的影响是致命的。
  4. 大量的寄存器,需要在指令的操作数中需要很多位表示,导致程序尺寸变大。
  5. 很多指令集在历史上就使用了很少的寄存器,出于兼容原因现在也很难改变。

实现方法简述

  1. tag索引的寄存器堆(tag-indexed register file)
  2. 保留站(reservation station)方法
  3. 通常是每个执行单元的输入口都有一个物理寄存器堆

相关寄存器部件

  1. 远期寄存器堆(Future File):
  2. 处理器对分支做投机执行的寄存器的状态保存于此。
  3. 使用逻辑寄存器号来索引访问。
  4. 历史缓冲区(History Buffer):
  5. 用于保存分支时的逻辑寄存器状态。
  6. 如果分支预测失败,将使用历史缓冲区的数据来恢复执行状态。
  7. 排缓冲区(Reorder Buffer,ROB):
  8. 为了实现指令的顺序提交,处理器内部使用了一个Buffer。如果在该缓冲区中排在一条指令之前的所有都已经提交,没有处于未提交状态的(称作in flight),则该指令也被提交(即确认执行完毕)。
  9. 因此重排缓冲区是在远期寄存器堆之后,体系结构寄存器堆之前。提交的指令的结果写入体系寄存器堆。
  10. 体系结构寄存器堆(Architectural Register File)或者引退寄存器堆(Retirement Register File,RRF):
  11. 存储了被提交的体系寄存器的状态。通过逻辑寄存器的号来查询这个寄存器堆。
  12. 重排序缓冲区(reorder buffer)中的引退(retired)或者说提交(committed)指令,把结果写入这个寄存器堆。

所属部件

  1. 编译器
  2. 会尽力检测出类似这样的问题,并把不同的寄存器分配给不同的指令使用。但是,受指令集体系结构的限制,汇编程序可以使用的寄存器名字的数量是有限的。
  3. 硬件实现
  4. 在处理器指令流水线执行时把这些指令集体系结构寄存器映射为不同的物理寄存器。
  5. 比如下图的Renamer / Allocator(也称为Resource Allocation Table (RAT))将架构寄存器映射到物理寄存器。 它还为loads and stores分配资源,并将uops分到不同端口。

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://zh.wikipedia.org/zh-cn/%E4%B9%B1%E5%BA%8F%E6%89%A7%E8%A1%8C

https://easyperf.net/blog/2018/04/22/What-optimizations-you-can-expect-from-CPU

Microarchitecture: Zero (one) idioms & Mov Elimination

微架构的关系

寄存器重命名是乱序执行Tomasulo算法的一部分

寄存器重命名可以实现: 1. 部分mov消除 2. NOPs 3. zero (one) idioms 对于这些指令,无序发射到scheduler。可以直接在reorder buffer写入结果。

Zero (one) idioms

Zero (one) idioms 是不管原寄存器src值是什么,结果/目的寄存器dst一直/一定是0 (1)的一类指令。比如:XOR一个寄存器和自己。

  1. 由于是在寄存器重命名阶段(Rename)时实现的
  2. 所以不需要发射到port执行单元执行,占用硬件资源。也没有延迟
  3. 但是需要划分前面部分的decode的带宽,和ROB(reorder buffer)的资源
    sub eax, eax
    xor eax, eax
    

例子

使用uarch-bench

xor eax, eax
dec edi
jnz .loop
由于第一条指令是Zero idioms;后两条指令可以macro-fusion。

所以各部分平均执行次数为

指令个数 UOPS_ISSUED UOPS_EXECUTED UOPS_RETIRED
3 2 1 2

特殊的情况

有些架构可能不支持srcImm0-dstReg的指令的Zero idioms

mov eax, 0 

mov Elimination

1. 由于是在寄存器重命名阶段(Rename)时实现的 1. 所以不需要发射到port执行单元执行,占用硬件资源。也没有延迟 2. 但是需要划分前面部分的decode的带宽,和ROB(reorder buffer)的资源

例子

add eax,4
mov ebx,eax ; //寄存器重命名,ebx指向eax即可
sub ebx,ecx
dec edi
jnz .loop
由于第二条指令是mov Elimination;后两条指令可以macro-fusion。

所以各部分平均执行次数为

指令个数 UOPS_ISSUED UOPS_EXECUTED UOPS_RETIRED
5 4 3 4

被覆盖的结果是否能消除

mov eax, 1 ; will be eliminated?
mov eax, 2 
dec edi
jnz .loop
第一个mov被覆盖了。这是属于编译器的工作。CPU做不到这点(即使做得到,为了实现这点设计的硬件开销也会很大,不值得)

无效操作是否能消除

一般和0的立即数作用有关

xor eax, eax 
sub ebx, eax ; will be eliminated? (eax is always 0)
第二条指令在IvyBridge也不会消除。这同样是编译器的工作

但是llvm-mca通过ZeroRegister的实现,可以消除。

类似的还有

mov eax, 0
mov ebx, 0
cmp eax, ebx ; eax and ebx are always equal
一般也不会消除。这同样是编译器的工作

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献

https://randomascii.wordpress.com/2012/12/29/the-surprising-subtleties-of-zeroing-a-register/

https://easyperf.net/blog/2018/04/22/What-optimizations-you-can-expect-from-CPU

https://zh.m.wikipedia.org/zh-hans/%E5%AF%84%E5%AD%98%E5%99%A8%E9%87%8D%E5%91%BD%E5%90%8D

Git Push 2 Homepage

ibug的网站部署思路

  1. 基于ibug.github.io
  2. 图片markdown两个仓库
  3. 对于acsa的网站
  4. 设置了action产生public/*.html
  5. 通过webhook来实现,服务器接收仓库的event信息。
    1. acsa的nginx接收location转发snode5
    2. snode5的nginx转发到127.0.0.2:9000上
    3. webhook.service接收到信息,然后git clone。并返回信息

hugo网站的action文件

根据公开的仓库,hugo的html文件会产生在gh-pages分支下

name: build

on:
  push:
    branches: [master]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
        with:
          #submodules: true  # Fetch Hugo themes (true OR recursive)
          fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod

      - name: Setup Hugo
        uses: peaceiris/actions-hugo@v2
        with:
          hugo-version: 'latest'
          #extended: true

      - name: Build
        run: hugo --minify

      - name: Deploy
        uses: peaceiris/actions-gh-pages@v3
        if: github.ref == 'refs/heads/master'
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./public

webhook的实现

接收端转发到内网的机器上(通过修改vim /etc/nginx/sites-enabled/default

server{
   location /_webhook/ {
                proxy_pass http://snode5.swangeese.fun;
                proxy_set_header Host $http_host;
    }
}
记得reload systemctl reload nginx

Nginx中location的作用是根据Url来决定怎么处理用户请求(转发请求给其他服务器处理或者查找本地文件进行处理)。location支持正则表达式,配置十分灵活。我们可以在一个虚拟主机(nginx中的一个server节点)下配置多个location以满足如动静分离,防盗链等需求。

在snode5上nginx也需要转发

 location /_webhook/ {
         proxy_pass http://127.0.0.2:9000;
     }

需要进一步的研究学习

暂无

遇到的问题

暂无

开题缘由、总结、反思、吐槽~~

参考文献