跳转至

笔记

Hash map

导言

IPCC比赛的时候,发现用hash map会比红黑树的map慢。感觉不对劲,我hash空间声明大,冲突不就低了吗?

Ubuntu server reInstall

思路

  1. 写U盘(见网上教程,注意备份U盘文件)
  2. 插U盘,开机前根据机器型号使用命令(F2,F4, F12或者Del)进入boit manager,选择从U盘boot
  3. 改引导启动顺序,把U盘启动改到最前面
  4. 根据提示安装ubuntu(服务器不用分区,默认就行,如果自己电脑双系统则需要根据内存设置分区)
  5. 设置root密码并进入root sudo passwd root
  6. 配置实验室网络

下载安装包

官网下载

重装系统怎么保留原本的磁盘文件

只要在安装系统时分出一个/home分区。你可以把Ubuntu的“/”分区看为Windows的C盘,重装Ubuntu时只格式化“/”分区,不格式化“/home”,这样就可以保留“/home”中的数据了。使用的时候就挂载就行

但是假如一开始没分区: 1. 可以插块新盘当作系统盘,系统安装好之后,把旧的文件系统挂载到新系统的某个目录下 2. 从旧的文件系统里划出一块区域,安装新系统。 1. 在你要安装的目标磁盘中,通过删除卷和删除分区操作腾出一块未分配的磁盘空间作为安装区

从已有分区中提取空间

xfs类型不可以直接缩减,只扩不减。如果是ext2,ext3,ext4可以在线缩减,如果xfs盘要缩小就要删除后重新添加

https://www.ywbj.cc/?p=712

非系统根分区LVM缩容

  1. 取消挂载 系统根分区无法在线取消挂载,所以这时请勿对系统根分区执行任何缩容操作
    umount /lv/ #取消挂载目录
    
  2. e2fsck检查修复磁盘完整性
    e2fsck -f /dev/vg0/lv0
    
  3. 缩小系统文件空间,即df -h查看的空间 不取消挂载,这步会报错,注:一定先减文件系统,再减逻辑卷
    resize2fs /dev/vg0/lv0 10G
    
  4. 缩小磁盘空间,即lsblk查看的空间 不执行上面的操作,直接执行这步,虽然成功,但是会操作数据丢失,系统无法启动。
    lvreduce -L 10G /dev/vg0/lv0
    
  5. 重新挂载
    mount -a
    
    最后查看lvs空间

系统根分区LVM缩容(救援模式)

正常启动系统进入救援模式 :启动按shift键,出现选择系统界面,按e。找到以单词 linux 开头的行,并在该行的末尾添加以下内容(要到达末尾,只需按下 CTRL+e 或使用 END 键或左右箭头键):

systemd.unit=rescue.target
添加完成后,只需按下 CTRL+xF10 即可继续启动救援模式。几秒钟后,你将以 root 用户身份进入救援模式(单用户模式)

需要进一步的研究学习

遇到的问题

暂无

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

参考文献

https://blog.csdn.net/weixin_40018873/article/details/109537532

https://www.ywbj.cc/?p=712

MPI Compilers

mpicc vs gcc

OpenMPI

# shaojiemike @ node5 in ~ [7:20:31]
$ mpicc -showme
gcc -I/usr/lib/x86_64-linux-gnu/openmpi/include/openmpi -I/usr/lib/x86_64-linux-gnu/openmpi/include -pthread -L/usr/lib/x86_64-linux-gnu/openmpi/lib -lmpi

IntelMPI

source /opt/intel/oneapi/setvars.sh

# shaojiemike @ node5 in ~/github/IPCC/SLIC on git:main x [15:45:06] C:1
$ mpicc -compile_info
gcc -I'/opt/intel/oneapi/mpi/2021.1.1/include' -L'/opt/intel/oneapi/mpi/2021.1.1/lib/release' -L'/opt/intel/oneapi/mpi/2021.1.1/lib' -Xlinker --enable-new-dtags -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib/release' -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib' -lmpifort -lmpi -lrt -lpthread -Wl,-z,now -Wl,-z,relro -Wl,-z,noexecstack -Xlinker --enable-new-dtags -ldl

> mpicc -v                  
mpigcc for the Intel(R) MPI Library 2021.5 for Linux*                                                                      
Copyright Intel Corporation.
Using built-in specs.       
COLLECT_GCC=gcc             
COLLECT_LTO_WRAPPER=/public1/soft/gcc/8.1.0/libexec/gcc/x86_64-pc-linux-gnu/8.1.0/lto-wrapper                              
Target: x86_64-pc-linux-gnu 
Configured with: ./configure --prefix=/public1/soft/gcc/8.1.0 --enable-languages=c,c++,fortran --disable-multilib          
Thread model: posix         
gcc version 8.1.0 (GCC) 

# shaojiemike @ node5 in ~/github/IPCC/SLIC on git:main x [15:45:16]
$ mpiicc -compile_info
icc -I'/opt/intel/oneapi/mpi/2021.1.1/include' -L'/opt/intel/oneapi/mpi/2021.1.1/lib/release' -L'/opt/intel/oneapi/mpi/2021.1.1/lib' -Xlinker --enable-new-dtags -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib/release' -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib' -lmpifort -lmpi -ldl -lrt -lpthread

# shaojiemike @ node5 in ~/github/IPCC/SLIC on git:main x [15:50:09] C:255
$ mpiicc -show
icc -I'/opt/intel/oneapi/mpi/2021.1.1/include' -L'/opt/intel/oneapi/mpi/2021.1.1/lib/release' -L'/opt/intel/oneapi/mpi/2021.1.1/lib' -Xlinker --enable-new-dtags -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib/release' -Xlinker -rpath -Xlinker '/opt/intel/oneapi/mpi/2021.1.1/lib' -lmpifort -lmpi -ldl -lrt -lpthread

MPICH

ipcc22_0029@ln121 ~/github/IPCC2022-preliminary/run (float_trick*) [10:49:48]
> mpicc -show         
gcc -I/public1/soft/mpich/3.1.4-gcc8.1.0/include -L/public1/soft/mpich/3.1.4-gcc8.1.0/lib -Wl,-rpath -Wl,/public1/soft/mpich/3.1.4-gcc8.1.0/lib -Wl,--enable-new-dtags -lmpi
For MPICH, according to the mpicc man pages, mpicc -compile_info shows the flags for compiling a program, and mpicc -link_info shows the flags for linking a program.

-showme (Open MPI) or -show (Open MPI, MPICH and derivates) use -showme:compile and -showme:link to obtain the options automatically

安装选项查看

intelmpi

> mpirun -info
HYDRA build details:
    Version:                                 2021.5
    Release Date:                            20211102 (id: 9279b7d62)
    Process Manager:                         pmi
    Bootstrap servers available:             ssh slurm rsh ll sge pbs pbsdsh pdsh srun lsf blaunch qrsh fork
    Resource management kernels available:   slurm ll lsf sge pbs cobalt

MPICH

ipcc22_0029@ln121 ~  [11:55:08]
> mpiexec --version               
HYDRA build details:
    Version:                                 3.1.4
    Release Date:                            Fri Feb 20 15:02:56 CST 2015
    CC:                              gcc    
    CXX:                             g++    
    F77:                             gfortran   
    F90:                             gfortran   
    Configure options:                       '--disable-option-checking' '--prefix=/public1/soft/mpich/3.1.4-gcc8.1.0' 'CC=gcc' 'CXX=g++' 'FC=gfortran' '--cache-file=/dev/null' '--srcdir=.' 'CFLAGS= -O2' 'LDFLAGS= ' 'LIBS=-lpthread ' 'CPPFLAGS= -I/public1/home/deploy/amd-mpich/mpich-3.1.4/src/mpl/include -I/public1/home/deploy/amd-mpich/mpich-3.1.4/src/mpl/include -I/public1/home/deploy/amd-mpich/mpich-3.1.4/src/openpa/src -I/public1/home/deploy/amd-mpich/mpich-3.1.4/src/openpa/src -D_REENTRANT -I/public1/home/deploy/amd-mpich/mpich-3.1.4/src/mpi/romio/include'
    Process Manager:                         pmi
    Launchers available:                     ssh rsh fork slurm ll lsf sge manual persist
    Topology libraries available:            hwloc
    Resource management kernels available:   user slurm ll lsf sge pbs cobalt
    Checkpointing libraries available:       
    Demux engines available:                 poll select

ipcc22_0029@ln121 ~  [11:55:31]
> mpichversion
MPICH Version:          3.1.4
MPICH Release date:     Fri Feb 20 15:02:56 CST 2015
MPICH Device:           ch3:nemesis
MPICH configure:        --prefix=/public1/soft/mpich/3.1.4-gcc8.1.0/ CC=gcc CXX=g++ FC=gfortran
MPICH CC:       gcc    -O2
MPICH CXX:      g++   -O2
MPICH F77:      gfortran   -O2
MPICH FC:       gfortran   -O2

OpenMPI

## 安装了IB支持
> ompi_info | grep openib
                 MCA btl: openib (MCA v2.1.0, API v3.0.0, Component v3.1.6)

> mpiexec --version      
mpiexec (OpenRTE) 4.1.1

Report bugs to http://www.open-mpi.org/community/help/

ipcc22_0029@ln121 ~/slurm/MPIInit  [12:24:19]
> module load mpi/openmpi/4.1.1-gcc7.3.0 
ipcc22_0029@ln121 ~/slurm/MPIInit  [12:24:51]
> mpicc -v                              
Using built-in specs.
COLLECT_GCC=/public1/soft/gcc/7.3.0/bin/gcc
COLLECT_LTO_WRAPPER=/public1/soft/gcc/7.3.0/libexec/gcc/x86_64-pc-linux-gnu/7.3.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: ./configure --prefix=/public1/soft/gcc/7.3.0 --disable-multilib
Thread model: posix
gcc version 7.3.0 (GCC) 

需要进一步的研究学习

暂无

遇到的问题

暂无

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

参考文献

https://stackoverflow.com/questions/11312719/how-to-compile-mpi-with-gcc

Why MPI_Init is slow

MPI_Init的作用

MPI_Init(&argc, &argv);

StackOverflow的回答是,Init在调用过程中初始化MPI库,并且在进程间建立通讯和编号。

知乎的回答: OpenMPI会在调用MPI_Init时按照你传递给mpirun的指令新建进程,而你传递给MPI_Init的参数,会被传递给新建的进程。

这似乎在暗示,两个进程不是同时产生和运行的。

猜想1

有顺序的观点是不成立的

即使有顺序 malloc的时间也没这么长。

猜想2

难道是malloc的数据需要MPI_Init复制一遍?

简单将MPI_Init提前到最开始,时间也基本没变,也不对。

如果单独写一个只有MPI_Init的程序,IntelMPI还是要耗时800ms

ipcc22_0029@ln121 ~/slurm/MPIInit  [11:42:32]
> srun -p IPCC -N 2 -n 2 -c 64 -t 1  MPI
MPIInit          took 882.110047 ms
MPIInit          took 892.112269 ms

测试比较超算上MPI_Init的时间

以IPCC2022初赛的北京超算云 AMD机器举例

mpirun的选择 mpi版本 GCC或者ICC的选择版本 超算运行 MPI_Init时间(ms)
IntelMPI mpi/intel/2022.1 gcc/10.2.0 只能sbatch,不能srun 1282.24 ~ 1678.59
OpenMPI mpi/openmpi/4.1.1-gcc7.3.0 2706ms~3235ms
MPICH mpich/3.1.4-gcc8.1.0 17ms
mpich/3.4.2 gcc/10.2.0 107ms

不能srun IntelMPI的问题

缺少一个环境变量

需要export I_MPI_PMI_LIBRARY=libpmi2.so

VTune 分析MPI的程序

https://www.intel.com/content/www/us/en/develop/documentation/vtune-help/top/analyze-performance/code-profiling-scenarios/mpi-code-analysis.html

https://www.intel.com/content/www/us/en/develop/documentation/vtune-cookbook/top/configuration-recipes/profiling-mpi-applications.html

环境变量

设置这个Intel mpi 1200 -> 1100

export PMI_TIME=1
export SLURM_PMI_KVS_NO_DUP_KEYS=yes

需要进一步的研究学习

实在是弄不懂,为什么不同的实现,时间差别这么大。可能慢是因为额外的通路设置,是为了之后的快速传输??

3.1.4的安装选项也看不到

> mpiexec --version     
mpiexec: error while loading shared libraries: libslurm.so.35: cannot open shared object file: No such file or directory

遇到的问题

暂无

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

参考文献

oneApi

安装

命令行安装

Intel OneAPI base toolkit

必须先安装base,可以看到默认安装的内容

这个GDB好像可以分析多进程

Intel OneAPI HPC toolkit

Intel OneAPI HPC toolkit包括了icc,icpc,ifort和OpenMP,IntelMPI还有MKL(Intel® oneAPI Math Kernel Library (oneMKL))

Download界面选择版本, 选择online或者offline会有推荐指令,如下

wget https://registrationcenter-download.intel.com/akdlm/irc_nas/18679/l_HPCKit_p_2022.2.0.191.sh

sudo sh ./l_HPCKit_p_2022.2.0.191.sh

icx icpx

> icx -v
Intel(R) oneAPI DPC++/C++ Compiler 2022.0.0 (2022.0.0.20211123)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /opt/intel/oneapi/compiler/2022.0.2/linux/bin-llvm
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Candidate multilib: .;@m64
Selected multilib: .;@m64
Found CUDA installation: /usr/local/cuda-11.5, version

> icpx --help
OVERVIEW: Intel(R) oneAPI DPC++/C++ Compiler

USAGE: clang++ [options] file...

需要进一步的研究学习

暂无

遇到的问题

暂无

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

参考文献

https://www.intel.com/content/www/us/en/develop/documentation/installation-guide-for-intel-oneapi-toolkits-linux/top/prerequisites/install-intel-gpu-drivers.html

Inline Assembly

GCC内联汇编

__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);
  1. __asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。
  2. __volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。
  3. Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。
  4. 但并非所有Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory");就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。
  5. 当在"Instruction List"中有多条指令的时候,需要用分号(;)或换行符(\n)将它们分开。
  6. 指令中的操作数可以使用占位符引用C语言变量,操作数占位符最多10个,名称如下:%0,%1,…,%9。指令中使用占位符表示的操作数,总被视为long型(4个字节),
    1. 但对其施加的操作根据指令可以是字或者字节,当把操作数当作字或者字节使用时,默认为低字或者低字节
    2. 对字节操作可以显式的指明是低字节还是次字节。方法是在%和序号之间插入一个字母,"b"代表低字节,"h"代表高字节,例如:%h1
  7. Output/Input
    1. 格式为形如"constraint"(variable)的列表(逗号分隔)。按照出现的顺序分别与指令操作数"%0","%1"对应
    2. 每个输出操作数的限定字符串必须包含"="表示他是一个输出操作数。例子"=r" (value)
  8. Clobber/Modify(由逗号格开的字符串组成)
    1. 在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作表达式使用"r"约束,让GCC为你选择一个寄存器时,GCC知道这些寄存器是被修改的,你根本不需要在Clobber/Modify域再声明它们。
    2. 但是对于"Instruction List"中的临时寄存器,需要在Clobber/Modify域声明这些寄存器或内存,让GCC知道修改了他们
      1. 例子:__asm__ ("mov R0, #0x34" : : : "R0");寄存器R0出现在"Instruction List中",并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定"R0",以让GCC知道这一点。
    3. Clobber/Modify域存在"memory",那么GCC会保证在此内联汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。

输入输出与指令的对应关系

寄存器约束符Operation Constraint

每一个Input和Output表达式都必须指定自己的操作约束Operation Constraint,这里将讨论在80386平台上所可能使用的操作约束。

当前的输入或输出需要借助一个寄存器时,需要为其指定一个寄存器约束,可以直接指定一个寄存器的名字。

常用的寄存器约束的缩写 约束 | 意义| |--------- | ----| r |表示使用一个通用寄存器,由 GCC 在%eax/%ax/%al,%ebx/%bx/%bl,%ecx/%cx/%cl,%edx/%dx/%dl中选取一个GCC认为合适的。 g |表示使用任意一个寄存器,由GCC在所有的可以使用的寄存器中选取一个GCC认为合适的。 q |表示使用一个通用寄存器,和约束r的意义相同。 a |表示使用%eax/%ax/%al b |表示使用%ebx/%bx/%bl c |表示使用%ecx/%cx/%cl d |表示使用%edx/%dx/%dl D |表示使用%edi/%di S |表示使用%esi/%si f |表示使用浮点寄存器 t |表示使用第一个浮点寄存器 u |表示使用第二个浮点寄存器

分类 |限定符 |描述 |--------- | ----|----------------| 通用寄存器 |“a”| 将输入变量放入eax 这里有一个问题:假设eax已经被使用,那怎么办?其实很简单:因为GCC 知道eax 已经被使用,它在这段汇编代码的起始处插入一条语句pushl %eax,将eax 内容保存到堆栈,然 后在这段代码结束处再增加一条语句popl %eax,恢复eax的内容 ||“b” |将输入变量放入ebx ||“c” |将输入变量放入ecx ||“d” |将输入变量放入edx ||“s” |将输入变量放入esi ||“d” |将输入变量放入edi ||“q” |将输入变量放入eax,ebx,ecx,edx中的一个 ||“r” |将输入变量放入通用寄存器,也就是eax,ebx,ecx,edx,esi,edi中的一个 ||"A"|把eax和edx合成一个64 位的寄存器(use long longs) 内存| “m” |内存变量 ||“o” |操作数为内存变量,但是其寻址方式是偏移量类型, 也即是基址寻址,或者是基址加变址寻址 ||“V”| 操作数为内存变量,但寻址方式不是偏移量类型 ||" "| 操作数为内存变量,但寻址方式为自动增量 ||“p” |操作数是一个合法的内存地址(指针) 寄存器或内存| “g” |将输入变量放入eax,ebx,ecx,edx中的一个 或者作为内存变量 ||“X” |操作数可以是任何类型 立即数 |“I” |0-31之间的立即数(用于32位移位指令) ||“J” |0-63之间的立即数(用于64位移位指令) ||“N” |0-255之间的立即数(用于out指令) ||“i” |立即数 ||“n” |立即数,有些系统不支持除字以外的立即数, 这些系统应该使用"n"而不是"i" 匹配 |" 0 ",“1” …“9” |, 表示用它限制的操作数与某个指定的操作数匹配,也即该操作数就是指定的那个操作数,例如"0"去描述"%1"操作数,那么"%1"引用的其实就是"%0"操作数,注意作为限定符字母的0-9 与 指令中的"%0"-"%9"的区别,前者描述操作数,后者代表操作数。 ||&; |该输出操作数不能使用过和输入操作数相同的寄存器 |操作数类型 |“=” |操作数在指令中是只写的(输出操作数) ||“+” |操作数在指令中是读写类型的(输入输出操作数) 浮点数| “f” |浮点寄存器 ||“t” |第一个浮点寄存器 ||“u” |第二个浮点寄存器| ||“G” |标准的80387浮点常数 ||% |该操作数可以和下一个操作数交换位置.例如addl的两个操作数可以交换顺序 (当然两个操作数都不能是立即数) ||# |部分注释,从该字符到其后的逗号之间所有字母被忽略 ||* |表示如果选用寄存器,则其后的字母被忽略

内存约束

如果一个Input/Output 操作表达式的C/C++表达式表现为一个内存地址,不想借助于任何寄存器,则可以使用内存约束。比如:

__asm__("lidt%0":"=m"(__idt_addr));
__asm__("lidt%0"::"m"(__idt_addr));

修饰符 |输入/输出 |意义 |---- | --- |--- = | O |表示此Output操作表达式是Write-Only的。 + | O |表示此Output操作表达式是Read-Write的。 & | O |表示此Output操作表达式独占为其指定的寄存器。 % | I |表示此Input 操作表达式中的C/C++表达式可以和下一 个Input操作表达式中的C/C++表达式互换

例子

Static __inline__ void __set_bit(int nr, volatile void * addr)
{
         __asm__(
                         "btsl %1,%0"
                         :"=m" (ADDR)
                         :"Ir" (nr));
}

第一个占位符%0与C 语言变量ADDR对应,第二个占位符%1与C语言变量nr对应。因此上面的汇编语句代码与下面的伪代码等价:btsl nr, ADDR

Clobber/Modify域存在"memory"的其他影响

使用"memory"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。

例如:

int main(int __argc, char* __argv[]) 
{ 
    int* __p = (int*)__argc; 
    (*__p) = 9999; 
    __asm__("":::"memory"); 
    if((*__p) == 9999) 
        return 5; 
    return (*__p); 
}
本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。

但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。

但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。

另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中

# define barrier() _asm__volatile_("": : :"memory")
主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是终执行的顺序,这个是处理器有关的。

Linux 源码例子

static inline char * strcpy(char * dest, const char *src)
{
    char *xdest = dest;
    __asm__ __volatile__
    ("1: \tmoeb %1@+, %0@+\n\t"   "jne 1b"  //这个冒号不是分隔符
    : "=a" (dest) , "=a" (stc)
    : "0"(dest), "1" (src)
     : "memory");
    return xdest;
}

需要进一步的研究学习

暂无

遇到的问题

暂无

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

参考文献

https://blog.csdn.net/yi412/article/details/80846083

https://www.cnblogs.com/elnino/p/4313340.html

Tar Zip Rar

各种指令的对比

命令 压缩空间效果 压缩时间效果 解压时间 说明
tar -cf 2.9G 13.8s 3.3s tar -cf archive.tar foo归档文件,没有压缩功能
tar -zcf 823M 1:44 19s tar -zcf archive.tar.gz foo归档并使用gzip压缩文件,gzip是zip的GNU实现,是最老的公开压缩方法
zip -1r 856M 48.6s 23.3s zip -1qr intel.zip intel, -1 compress faster,unzip解压
zip -9r 824M 11:19 24s 压缩这也太慢了吧
rar a 683M 2:02 46s unrar x解压

上述测试基于大小3GB的文件夹

关于rar

虽然说好像有专利的软件,但是sudo apt install rar貌似就可以安装。但是空间效果确实还行多压1/3,但是时间要多两倍。

还是建议tar.gz

  • 因为 7z 和 zip 压缩格式都不能保留 unix 风格的文件权限,比如解压出个可执行文件要重新 chmod chown 才能恢复正常。
  • 而 tar 格式可以。而 tar 本身不提供压缩,无非就是把包括所有文件的內容和权限拼成一个文件而己,所以用另外如 gzip 格式压缩。为什么是 gzip,因为几乎所有 linux 都支持而已。

参考文献

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

需要进一步的研究学习

暂无

遇到的问题

暂无

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

参考文献