跳转至

笔记

AMD CPU

AMD history

超微半导体公司(英語:Advanced Micro Devices, Inc.;縮寫:AMD、超微,或譯「超威」),創立於1969年,是一家專注於微处理器及相關技術設計的跨国公司,总部位于美國加州舊金山灣區矽谷內的森尼韦尔市。

AMD EPYC 7452 32-Core Processor

由 AMD 于 2019 年年中设计和推出。 是基于 Zen 2 微架构的多芯片处理器

> cat lscpu.txt              
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                64
On-line CPU(s) list:   0-63
Thread(s) per core:    1
Core(s) per socket:    32
Socket(s):             2
NUMA node(s):          2
Vendor ID:             AuthenticAMD
CPU family:            23
Model:                 49
Model name:            AMD EPYC 7452 32-Core Processor
Stepping:              0
CPU MHz:               2345.724
BogoMIPS:              4691.44
Virtualization:        AMD-V
L1d cache:             32K
L1i cache:             32K
L2 cache:              512K
L3 cache:              16384K
NUMA node0 CPU(s):     0-31
NUMA node1 CPU(s):     32-63
Flags:               
(Intel) fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht 

(AMD)   syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm 

        constant_tsc art rep_good nopl nonstop_tsc extd_apicid aperfmperf eagerfpu 

(intel) pni pclmulqdq monitor ssse3 fma cx16 sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand 

(AMD)   lahf_lm cmp_legacy svm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw ibs skinit wdt tce topoext perfctr_core perfctr_nb bpext perfctr_l2 

        cpb cat_l3 cdp_l3 hw_pstate sme retpoline_amd 

        ssbd ibrs ibpb stibp 

        vmmcall 

(intel) fsgsbase bmi1 avx2 smep bmi2 cqm rdt_a rdseed adx smap clflushopt clwb sha_ni 

        xsaveopt xsavec xgetbv1 

(intel) cqm_llc cqm_occup_llc cqm_mbm_total cqm_mbm_local 

(AMD)   clzero irperf xsaveerptr 

        arat 

(AMD)   npt lbrv svm_lock nrip_save tsc_scale vmcb_clean flushbyasid decodeassists pausefilter pfthreshold avic v_vmsave_vmload vgif 

(intel) umip 

(AMD)   overflow_recov succor smca

CPU\Thread\Socket

  1. CPU(s):64 = the number of logical cores = “Thread(s) per core” × “Core(s) per socket” × “Socket(s)” = 1 * 32 * 2
  2. One socket is one physical CPU package (which occupies one socket on the motherboard);
  3. each socket hosts a number of physical cores, and each core can run one or more threads.
  4. In this case, you have two sockets, each containing a 32-core AMD EPYC 7452 CPU, and since that not supports hyper-threading, each core just run a thread.

CPU flags

Intel-defined CPU features, CPUID level 0x00000001 (edx)

fpu:板载 FPU(浮点支持)
vme:虚拟 8086 模式增强功能
de: 调试扩展 (CR4.DE)
pse:页表大小扩展(4MB 内存页表)
tsc:时间戳计数器(RDTSC)
msr:特定模型的寄存器(RDMSR、WRMSR)
pae:物理地址扩展(支持超过 4GB 的 RAM)
mce:机器检查异常
cx8:CMPXCHG8 指令(64 位比较和交换)
apic:板载 APIC(Advanced Programmable Interrupt Controller)
sep:SYS ENTER/SYS EXIT
mtrr:内存类型范围寄存器
pge:页表全局启用(PDE 和 PTE 中的全局位)
mca:机器检查架构
cmov:CMOV 指令(条件移动)(也称为 FCMOV)
pat:页表属性表
pse36:36 位 PSE(大页表)
pn:处理器序列号
clflush:缓存行刷新指令
mmx:多媒体扩展
fxsr: FXSAVE/FXRSTOR, CR4.OSFXSR #  enables Streaming SIMD Extensions (SSE) instructions and fast FPU save & restore.
sse:英特尔 SSE 矢量指令
sse2:sse2
ht:超线程和/或多核
没有使用到的
ss:CPU自监听
tm:自动时钟控制(Thermal Monitor)
ia64:英特尔安腾架构 64 位(不要与英特尔的 64 位 x86 架构混淆,标志为 x86-64 或由标志 lm 指示的“AMD64”位)
pbe:Pending Break Enable(PBE# 引脚)唤醒支持

AMD-defined CPU features, CPUID level 0x80000001

syscall: SYSCALL (Fast System Call) and SYSRET (Return From Fast System Call)
nx:执行禁用 # NX 位(不执行)是 CPU 中使用的一项技术,用于分隔内存区域,以供处理器指令(代码)存储或数据存储使用
mmxext: AMD MMX extensions
fxsr_opt: FXSAVE/FXRSTOR optimizations
pdpe1gb: One GB pages (allows hugepagesz=1G)
rdtscp: Read Time-Stamp Counter and Processor ID
lm: Long Mode (x86-64: amd64, also known as Intel 64, i.e. 64-bit capable)
没有使用到的
mp: Multiprocessing Capable.
3dnowext: AMD 3DNow! extensions
3dnow: 3DNow! (AMD vector instructions, competing with Intel's SSE1)

Other features, Linux-defined mapping(映射?)

constant_tsc:TSC(Time Stamp Counter) 以恒定速率滴答
art: Always-Running Timer
rep_good:rep 微码运行良好
nopl: The NOPL (0F 1F) instructions # NOPL is long-sized bytes "do nothing" operation
nonstop_tsc: TSC does not stop in C states
extd_apicid: has extended APICID (8 bits) (Advanced Programmable Interrupt Controller)
aperfmperf: APERFMPERF # On x86 hardware, APERF and MPERF are MSR registers that can provide feedback on current CPU frequency.
eagerfpu: Non lazy FPU restore

Intel-defined CPU features, CPUID level 0x00000001 (ecx)

pni: SSE-3 (“2004年,新内核Prescott New Instructions”)
pclmulqdq: 执行四字指令的无进位乘法 - GCM 的加速器)
monitor: Monitor/Mwait support (Intel SSE3 supplements)
ssse3:补充 SSE-3
fma:融合乘加
cx16: CMPXCHG16B # double-width compare-and-swap (DWCAS) implemented by instructions such as x86 CMPXCHG16B
sse4_1:SSE-4.1
sse4_2:SSE-4.2
x2apic: x2APIC
movbe:交换字节指令后移动数据
popcnt:返回设置为1指令的位数的计数(汉明权,即位计数)
aes/aes-ni:高级加密标准(新指令)
xsave:保存处理器扩展状态:还提供 XGETBY、XRSTOR、XSETBY
avx:高级矢量扩展
f16c:16 位 fp 转换 (CVT16)
rdrand:从硬件随机数生成器指令中读取随机数

More extended AMD flags: CPUID level 0x80000001, ecx

lahf_lm:在长模式下从标志 (LAHF) 加载 AH 并将 AH 存储到标志 (SAHF)
cmp_legacy:如果是,超线程无效
svm:“安全虚拟机”:AMD-V
extapic:扩展的 APIC 空间
cr8_legacy:32 位模式下的 CR8
abm:高级位操作
sse4a:SSE-4A
misalignsse:指示当一些旧的 SSE 指令对未对齐的数据进行操作时是否产生一般保护异常 (#GP)。还取决于 CR0 和对齐检查位
3dnowprefetch:3DNow预取指令
osvw:表示 OS Visible Workaround,它允许 OS 绕过处理器勘误表。
ibs:基于指令的采样
xop:扩展的 AVX 指令
skinit:SKINIT/STGI 指令 # x86虚拟化的系列指令
wdt:看门狗定时器
tce:翻译缓存扩展
topoext:拓扑扩展 CPUID 叶
perfctr_core:核心性能计数器扩展
perfctr_nb:NB 性能计数器扩展
bpext:数据断点扩展
perfctr_l2:L2 性能计数器扩展

辅助标志:Linux 定义 - 用于分散在各种 CPUID 级别的功能

cpb:AMD 核心性能提升
cat_l3:缓存分配技术L3
cdp_l3:代码和数据优先级 L3
hw_pstate:AMD HW-PSstate Hardware P-state
sme:AMD 安全内存加密
retpoline_amd:AMD Retpoline 缓解 # 防止被攻击的安全策略

Virtualization flags: Linux defined

vmmcall:比 VMCALL 更喜欢 VMMCALL

Intel-defined CPU features, CPUID level 0x00000007:0 (ebx)

fsgsbase:{RD/WR}{FS/GS}BASE 指令
bmi1:第一 组位操作扩展
avx2: AVX2 instructions
smep:主管模式执行保护
bmi2:第二 组位操作扩展
cqm:缓存 QoS 监控(Quality of Service )
rdt_a:资源总监技术分配
rdseed:RDSEED 指令,RDRAND 用于仅需要高质量随机数的应用程序
adx:ADCX 和 ADOX 指令
smap:超级用户模式访问保护
clflushopt:CLFLUSHOPT 指令, Optimized CLFLUSH,优化的缓存行刷回, 能够把指定缓存行(Cache Line)从所有级缓存中淘汰,若该缓存行中的数据被修改过,则将该数据写入主存;支持现状:目前主流处理器均支持该指令。
clwb: CLWB instruction (Cache Line Write Back,缓存行写回)作用与 CLFLUSHOPT 相似,但在将缓存行中的数据写回之后,该缓存行仍将呈现为未被修改过的状态;支持现状
sha_ni: SHA1/SHA256 Instruction Extensions

扩展状态功能,CPUID 级别 0x0000000d:1 (eax)

xsaveopt: Optimized XSAVE
xsavec: XSAVEC 使用压缩保存处理器扩展状态
xgetbv1: XGETBV with ECX = 1

Intel-defined CPU QoS sub-leaf, CPUID level 0x0000000F:0 (edx)

cqm_llc: LLC QoS # last level cache (LLC)
cqm_occup_llc: LLC occupancy monitoring #  Memory Bandwidth Monitoring (MBM)
cqm_mbm_total: LLC total MBM monitoring
cqm_mbm_local: LLC local MBM monitoring

AMD-defined CPU features, CPUID level 0x80000008 (ebx)

clzero:CLZERO 指令,随 Zen 微体系结构引入的 AMD 供应商特定 x86 指令。CLZERO 通过向行中的每个字节写入零来清除由 RAX 寄存器中的逻辑地址指定的缓存行。
irperf:指令退休性能计数器
xsaveerptr:始终保存/恢复 FP 错误指针

Thermal and Power Management leaf, CPUID level 0x00000006 (eax)

arat: Always Running APIC Timer

AMD SVM 特征识别,CPUID 级别 0x8000000a (edx)

npt:AMD 嵌套页表支持
lbrv:AMD LBR 虚拟化支持
svm_lock:AMD SVM 锁定 MSR
nrip_save:AMD SVM next_rip 保存
tsc_scale:AMD TSC 缩放支持
vmcb_clean:AMD VMCB 清洁位支持
flushbyasid:AMD 逐个 ASID 支持
解码辅助:AMD 解码辅助支持
pausefilter: AMD 过滤暂停拦截
pfthreshold:AMD 暂停过滤器阈值
avic:虚拟中断控制器
vmsave_vmload:虚拟 VMSAVE VMLOAD
vgif:虚拟 GIF

Intel-defined CPU features, CPUID level 0x00000007:0 (ecx)

umip:用户模式指令保护

AMD-defined CPU features, CPUID level 0x80000007 (ebx)

overflow_recov:MCA 溢出恢复支持 # Machine Check Architecture (MCA)
succor:不可纠正的错误控制和恢复
smca:可扩展的 MCA

不知道的flags

ssbd ibrs ibpb stibp

Processor P-states and C-states

英特尔处理器支持多种技术来优化功耗。 在本文中,我们概述了 p 状态(运行期间电压和 CPU 频率的优化)和 c 状态(如果内核不必执行任何指令,则优化功耗)。

ADCX 和 ADOX

ADCX 将两个无符号整数加上进位,从进位标志中读取进位,并在必要时将其设置在那里。 不影响进位以外的其他标志。 ADOX 将两个无符号整数加上进位,从溢出标志中读取进位,并在必要时将其设置在那里。 不影响溢出以外的其他标志。

需要进一步的研究学习

暂无

遇到的问题

暂无

参考文献

https://unix.stackexchange.com/questions/43539/what-do-the-flags-in-proc-cpuinfo-mean

Bash Scripting

简介

记录一下bash脚本编写的东东,可以参考cheatsheet

调试打印

在Bash脚本里打开打印,并利用shellprof./shellprof ./testcase.sh计算时间。

#!/bin/bash

# 使用 trap 来捕获 DEBUG 信号,每次执行命令前触发
trap 'echo "$BASH_COMMAND"' DEBUG

# 设置调试模式打印前缀PS4,包含时间戳和行号
export PS4='+ $(date "+%Y-%m-%d %H:%M:%S.%3N")\011 '

set -x  # 开启调试模式

基础知识

参数

  • $? 是上一个指令执行的状态值
  • $@ 代表shell脚本的命令行参数. $1 , $2 , etc., 分别代表命令行传入的第一个和第二个参数。举例,对于命令./run.sh XXX YYY,$1代表XXX , $2代表YYY
#匹配并删除最短前缀
str="/path/to/foo.cpp"
echo "${str%.cpp}"    # /path/to/foo
echo "${str%.cpp}.o"  # /path/to/foo.o
echo "${str%/*}"      # /path/to

echo "${str##*.}"     # cpp (extension)
echo "${str##*/}"     # foo.cpp (basepath)

echo "${str#*/}"      # path/to/foo.cpp
echo "${str##*/}"     # foo.cpp

在 Bash 脚本中,### 符号用于字符串操作,具体用于删除前缀部分:

  1. ${str#pattern}:删除变量 str 中最短匹配 pattern 的前缀。
  2. ${str##pattern}:删除变量 str 中最长匹配 pattern 的前缀。

例如:

  1. echo "${str##*.}":这行代码使用 ## 删除了最后一个 . 及其前面的所有内容,从而只保留了文件扩展名 cpp。这是因为 * 匹配所有字符。
  2. echo "${str##*/}":这行代码使用 ## 删除了最后一个 / 及其前面的所有内容,因此只保留了文件名 foo.cpp
  3. ${str#*/} 删除了第一个 / 之前的部分,结果是 path/to/foo.cpp
  4. ${str##*/} 删除了最后一个 / 之前的所有部分,结果是 foo.cpp

这两个符号用于简化路径处理和提取文件名等。

变量作用域

  • Bash 中的变量是 全局变量,也就是说,在脚本中的任何地方都可以访问到这个变量,除非使用 local 关键字将其作用域限制在特定的函数中。
  • local 声明的变量确实限制了它的作用域,但作用域仅限制在当前函数及其子函数(嵌套函数)内。对于嵌套函数,还是共用(读取和修改)上层的local变量。

函数传参

  • Bash里的函数传参和脚本的命令行参数也是共用$@$1等,但是默认是函数的局部变量。
  • 如果忘记使用局部变量,这常会导致一个问题:
    • 外层的for循环的i被内层函数的for循环i修改导致循环异常。
    • 解决方案:需要将内层函数的for循环i定义为local,来与上层的隔离。

其他

  • /dev/null是一个几乎不管向它写入什么,都只返回成功,但是什么都没真的写入的文件。换句话说就是个“无底洞”,扔进去的东西肯定算扔进去了,但是扔进去就看不见了。

Environment

# read
SCRIPT_DIR="$(dirname $0)"

# set
export HOSTNAME="$(hostname)"

# check Environment
if [[ -z "${ITHEMAL_HOME}" ]]; then
    echo "ITHEMAL_HOME environment variable must be set!"
    exit 1
fi

实例

判断docker是否启动
function container_id() {
    sudo docker ps -q --filter 'name=ithemal$'
}

CONTAINER="$(container_id)"

if [[ -z "${CONTAINER}" ]]; then
    read -p "Container is not currently running. Would you like to start it? (y/n) " -r

    if [[ !($REPLY =~ ^[Yy]) ]]; then
    echo "Not starting."
    exit 1
    fi

    # others
fi
尝试获取sudo权限
function get_sudo() {
    if ! sudo -S true < /dev/null 2> /dev/null; then
        echo "sudo access required for docker:"
        sudo true
    fi
}
重连或者创建一个到名称为$1tmux窗口
#!/usr/bin/env bash

SESSION=$(tmux ls -F '#S #{session_attached}' | grep ' 0$' | head -n 1 | awk '{$NF=""; print $0}' | awk '{$1=$1;print}')

if [ ! -z "${SESSION}" ]; then
    tmux attach -t "${SESSION}"
else
    tmux new "bash -l"
fi

参考文献

pip

pip + 换源 ,能使用export http_proxy (:多试几次

pip install xxx -i https://mirrors.aliyun.com/pypi/simple/ --trusted-host=mirrors.aliyun.com 或者设置文件

也可以通过环境变量

export PIP_INDEX_URL=https://mirrors.aliyun.com/pypi/simple/
export PIP_TRUSTED_HOST=mirrors.aliyun.com

Rust

简介

Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。

Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。

安装

异常简单,默认安装在自己.local/bin下,会自动修改bashrc/zshrc On Linux and macOS systems, this is done as follows:

curl https://sh.rustup.rs -sSf | sh

基础语法

printf

impl ClassName {
    pub fn printFunc() {
        let a = 12;
        println!("a is {0}, a again is {0}", a); 
        //println 不是一个函数,而是一个宏规则。所以有感叹号
    }
}

变量

Rust 是强类型语言,但具有自动判断变量类型的能力。

//可以指定类型
let a: u64 = 123;
//不可变变量
let a = 123;
let a = 456; //不是复制是,重新绑定
let s2 = s1.clone(); //这才是真复制
//变量
let mut a = 123;
a = 456;
//常量
const a: i32 = 123;

函数

函数返回值

Rust 函数声明返回值类型的方式:在参数声明之后用 -> 来声明函数返回值的类型(不是 : )。

不写return是将最后一个当作返回值?(貌似是

Rust是如何实现内存安全的呢?

内存安全

  1. buffer overflow
  2. null pointer dereference
  3. use after free
  4. use of uninitialized memory
  5. illegal free (of an already-freed pointer, or a non-malloced pointer)

所有权

所有权对大多数开发者而言是一个新颖的概念,它是 Rust 语言为高效使用内存而设计的语法机制。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。

所有权三规则
  1. Rust 中的每个值都有一个变量,称为其所有者。
  2. 一次只能有一个所有者。
  3. 当所有者不在程序运行范围时,该值将被删除。
原理

如果我们定义了一个变量并给它赋予一个值,这个变量的值存在于内存中。这种情况很普遍。但如果我们需要储存的数据长度不确定(比如用户输入的一串字符串),我们就无法在定义时明确数据长度,也就无法在编译阶段令程序分配固定长度的内存空间供数据储存使用。(有人说分配尽可能大的空间可以解决问题,但这个方法很不文明)。这就需要提供一种在程序运行时程序自己申请使用内存的机制——堆。本章所讲的所有"内存资源"都指的是堆所占用的内存空间。

有分配就有释放,程序不能一直占用某个内存资源。因此决定资源是否浪费的关键因素就是资源有没有及时的释放。

我们把字符串样例程序用 C 语言等价编写:

{
    char *s = (char *)malloc(sizeof(char)*10);
    s = "nhooo"; //伪代码了
    free(s); // 释放 s 资源
}

很显然,Rust 中没有调用 free 函数来释放字符串 s 的资源(假设 "nhooo" 在堆中,这里)。Rust 之所以没有明示释放的步骤是因为在变量范围结束的时候,Rust 编译器自动添加了调用释放资源函数的步骤

这种机制看似很简单了:它不过是帮助程序员在适当的地方添加了一个释放资源的函数调用而已。但这种简单的机制可以有效地解决一个史上最令程序员头疼的编程问题。

https://hashrust.com/blog/memory-safey-in-rust-part-1/

https://deathking.github.io/2020/08/03/blue-team-rust-what-is-memory-safety-really/

https://segmentfault.com/a/1190000041151698

https://bbs.huaweicloud.com/blogs/193974

需要进一步的研究学习

暂无

遇到的问题

暂无

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

参考文献

ML Optimizer

导言

  • 优化算法(Optimizer)目标是优化(最小化或最大化)一个损失函数,以调整模型参数,使模型在训练数据上表现得更好。
  • 在深度学习中,优化算法是训练神经网络时至关重要的组成部分,它们决定了模型参数如何更新以最小化损失。
  • 所以梯度下降、动量法、随机梯度下降、RMSprop、Adam、AdamW、LAMB等算法都是优化算法。

Safe File Transport

use OpenSSL to encrypt file by RSA key

已有文件

  1. 未加密文件 secretfile.txt
  2. 接受方的公钥 recipients-key.pub

产生一次性加密解密对称密钥secret.key

256 bit (32 byte) random key

openssl rand -out secret.key 32

使用secret.key 加密所需文件

生成加密后文件secretfile.txt.enc

openssl aes-256-cbc -in secretfile.txt -out secretfile.txt.enc -pass file:secret.key

使用公钥recipients-key.pub加密secret.key

得到加密过的一次性key : secret.key.enc

openssl rsautl -encrypt -oaep -pubin -inkey <(ssh-keygen -e -f recipients-key.pub -m PKCS8) -in secret.key -out secret.key.enc
<()是子进程的意思。
 rm secret.key

发送加密文件

加密文件 secretfile.txt.enc和 secret.key.enc

use OpenSSL to descrypt file

已有文件

  1. 加密文件 secretfile.txt.enc和 secret.key.enc
  2. 接受方的私钥

解密secret.key.enc

 openssl rsautl -decrypt -oaep -inkey ~/.ssh/id_rsa -in secret.key.enc -out secret.key

解密secretfile.txt.enc

openssl aes-256-cbc -d -in secretfile.txt.enc -out secretfile.txt -pass file:secret.key

需要进一步的研究学习

  1. 使用 publickey.pem 可以看这篇 https://blog.csdn.net/makenothing/article/details/54645578
  2. id_rsa.pub.pem产生可以看这个 https://www.czeskis.com/random/openssl-encrypt-file.html

遇到的问题

暂无

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

参考文献

https://www.bjornjohansen.com/encrypt-file-using-ssh-key

https://blog.csdn.net/makenothing/article/details/54645578

Nvidia Arch : Ampere & Hopper & Pascal

基本概念

GPU Processing Clusters (GPCs),

Texture Processing Clusters (TPCs),

Streaming Multiprocessors (SMs)

  • CUDA cores: basic integer/floating point arithmetic – high throughput, low latency
  • Load/Store (LD/ST): issues memory accesses to appropriate controller – possibly high latency
  • Special Function Unit (SFU): trigonometric math functions, etc – reduced throughput
  • special tensor cores (Since Turing and Volta): have specialized matrix arithmetic capabilities

H100

GH100

img

上面两张图组成一个SM,Special Function Units (SFUs)

P40

GP102

图中红框是一个SM

A100

GA100

RTX 3090

10496个流处理器,核心加速频率1.70GHz,384-bit 24GB GDDR6X显存。

GA102

在之前的GA100大核心中,每组SM是64个INT32单元、64个FP32单元及32个FP64单元组成的,但在GA102核心中,FP64单元大幅减少,增加了RT Core,Tensor Core也略微减少。

游戏卡与专业卡的区别

  1. 应用方面不同
    1. 游戏卡会对三维图像处理有特殊处理,有光线追踪单元
    2. 专业计算卡,可能对某些格式的解压压缩有特殊单元,或者对半精度计算有特殊支持。
  2. 做工不同
    1. 专业卡由于在服务器上24小时不同工作,在多相供电,散热都堆料处理,游戏卡不同(公版,非公版肯定不一样)
  3. 驱动不同
    1. 游戏卡对应游戏软件的优化驱动,专业卡有对专业软件的驱动支持
  4. 价格不同
    1. 专业卡贵4倍不止。
  5. 参数的不同,对于同一颗核心(以RTX3090与A100 40G举例)
    1. A100的GA100是8块完整的,GA102是7块。
    2. A100领先的地方 1. 堆料完爆对手 2. 显存往往更多,AI应用 3. 访存更快 4. 支持 High bandwidth memory (HBM) 5. 在多精度和半精度有优势(NVIDIA A100 SXM4 40 GB VS.NVIDIA GeForce RTX 3090)
    3. RTX3090领先的地方 1. 频率更高 2. 有视频输出接口,支持OpenGL,DirectX 3. 有RT core 光追

参考文献

https://zhuanlan.zhihu.com/p/394352476

OpenMP

线程绑定

OpenMP 4.0 提供 OMP_PLACESOMP_PROC_BIND 环境变量来指定程序中的 OpenMP 线程如何绑定到处理器。这两个环境变量通常结合使用。OMP_PLACES 用于指定线程将绑定到的计算机位置(硬件线程、核心或插槽)。OMP_PROC_BIND 用于指定绑定策略(线程关联性策略),这项策略指定如何将线程分配到位置。

除了 OMP_PLACESOMP_PROC_BIND 这两个环境变量外,OpenMP 4.0 还提供可在 parallel 指令中使用的 proc_bind 子句。proc_bind 子句用于指定如何将执行并行区域的线程组绑定到处理器。

SlURM MPI OpenMP绑定方法参考清华的文档

OMP_NUM_THREADS=28 OMP_PROC_BIND=true OMP_PLACES=cores:每个线程绑定到一个 core,使用默认的分布(线程 n 绑定到 core n);
OMP_NUM_THREADS=2 OMP_PROC_BIND=true OMP_PLACES=sockets:每个线程绑定到一个 socket;
OMP_NUM_THREADS=4 OMP_PROC_BIND=close OMP_PLACES=cores:每个线程绑定到一个 core,线程在 socket 上连续分布(分别绑定到 core 0,1,2,3;
OMP_NUM_THREADS=4 OMP_PROC_BIND=spread OMP_PLACES=cores:每个线程绑定到一个 core,线程在 socket 上尽量散开分布(分别绑定到 core 0,7,14,21;
lscpu结合htop观察
NUMA 节点0 CPU:                 0-15,32-47              
NUMA 节点1 CPU:                 16-31,48-63

编译制导格式

静态扩展 * 文本代码在一个编译制导语句之后,被封装到一个结构块中

孤立语句 * 一个OpenMP的编译制导语句不依赖于其它的语句

parallel

并行域中的代码被所有的线程执行

for

for语句指定紧随它的循环语句必须由线程组并行执行;

sections

sections编译制导语句指定内部的代码被划分给线程组中的各线程

不同的section由不同的线程执行

single

single编译制导语句指定内部代码只有线程组中的一个线程执行。

线程组中没有执行single语句的线程会一直等待代码块的结束,使用nowait子句除外

来自 https://ppc.cs.aalto.fi/ch3/nowait/

组合parallel for / parallel sections 编译制导语句

  1. Parallel for编译制导语句表明一个并行域包含一个独立的for语句
  2. parallel sections编译制导语句表明一个并行域包含单独的一个sections语句

同步结构

  1. master 制导语句
  2. 指定代码段只有主线程执行
  3. critical制导语句
  4. critical制导语句表明域中的代码一次只能执行一个线程,其他线程被阻塞在临界区
  5. 语句格式:#pragma omp critical [name] newline
  6. barrier制导语句
  7. 同步一个线程组中所有的线程,先到达的线程在此阻塞,等待其他线程
  8. atomic制导语句
  9. 指定特定的存储单元将被原子更新
  10. #pragma omp atomic x++;
  11. flush制导语句
  12. 标识一个同步点,用以确保所有的线程看到一致的存储器视图
  13. ordered制导语句
  14. 相对于critical,多了一个顺序
  15. 只能出现在for或者parallel for语句的动态范围中
  16. threadprivate语句使一个全局文件作用域的变量在并行域内变成每个线程私有
  17. 每个线程对该变量复制一份私有拷贝

critical vs atomic

The fastest way is neither critical nor atomic. Approximately, addition with critical section is 200 times more expensive than simple addition, atomic addition is 25 times more expensive then simple addition.(maybe no so much expensive, the atomic operation will have a few cycle overhead (synchronizing a cache line) on the cost of roughly a cycle. A critical section incurs the cost of a lock.)

The fastest option (not always applicable) is to give each thread its own counter and make reduce operation when you need total sum.

critical vs ordered

omp critical is for mutual exclusion(互斥), omp ordered refers to a specific loop and ensures that the region executes sequentually in the order of loop iterations. Therefore omp ordered is stronger than omp critical, but also only makes sense within a loop.

omp ordered has some other clauses, such as simd to enforce the use of a single SIMD lane only. You can also specify dependencies manually with the depend clause.

Note: Both omp critical and omp ordered regions have an implicit memory flush at the entry and the exit.

ordered example

vector<int> v;

#pragma omp parallel for ordered schedule(dynamic, anyChunkSizeGreaterThan1)
    for (int i = 0; i < n; ++i){
            ...
            ...
            ...
#pragma omp ordered
            v.push_back(i);
    }
tid  List of     Timeline
     iterations
0    0,1,2       ==o==o==o
1    3,4,5       ==.......o==o==o
2    6,7,8       ==..............o==o==o

= shows that the thread is executing code in parallel. o is when the thread is executing the ordered region. . is the thread being idle, waiting for its turn to execute the ordered region.

With schedule(static,1) the following would happen:

tid  List of     Timeline
     iterations
0    0,3,6       ==o==o==o
1    1,4,7       ==.o==o==o
2    2,5,8       ==..o==o==o

语句绑定与语句嵌套规则

Clauses 子句

见 https://docs.microsoft.com/en-us/cpp/parallel/openmp/reference/openmp-clauses?view=msvc-160

#pragma omp parallel for collapse(2)
for( int y = y1; y < y2; y++ )
{
    for( int x = x1; x < x2; x++ )
    {

schedule

------------------------------------------------
| static | static | dynamic | dynamic | guided |
|    1   |    5   |    1    |    5    |        |
------------------------------------------------
|    0   |    0   |    0    |    2    |    1   |
|    1   |    0   |    3    |    2    |    1   |
|    2   |    0   |    3    |    2    |    1   |
|    3   |    0   |    3    |    2    |    1   |
|    0   |    0   |    2    |    2    |    1   |
|    1   |    1   |    2    |    3    |    3   |
|    2   |    1   |    2    |    3    |    3   |
|    3   |    1   |    0    |    3    |    3   |
|    0   |    1   |    0    |    3    |    3   |
|    1   |    1   |    0    |    3    |    2   |
|    2   |    2   |    1    |    0    |    2   |
|    3   |    2   |    1    |    0    |    2   |
|    0   |    2   |    1    |    0    |    3   |
|    1   |    2   |    2    |    0    |    3   |
|    2   |    2   |    2    |    0    |    0   |
|    3   |    3   |    2    |    1    |    0   |
|    0   |    3   |    3    |    1    |    1   |
|    1   |    3   |    3    |    1    |    1   |
|    2   |    3   |    3    |    1    |    1   |
|    3   |    3   |    0    |    1    |    3   |
------------------------------------------------

private vs firstprivate vs lastprivate

private variables are not initialised, i.e. they start with random values like any other local automatic variable

firstprivate initial the value as the before value.

lastprivate save the value to the after region. 这个last的意思不是实际最后运行的一个线程,而是调度发射队列的最后一个线程。从另一个角度上说,如果你保存的值来自随机一个线程,这也是没有意义的。 firstprivate and lastprivate are just special cases of private

#pragma omp parallel
{
   #pragma omp for lastprivate(i)
      for (i=0; i<n-1; i++)
         a[i] = b[i] + b[i+1];
}
a[i]=b[i];

private vs threadprivate

A private variable is local to a region and will most of the time be placed on the stack. The lifetime of the variable's privacy is the duration defined of the data scoping clause. Every thread (including the master thread) makes a private copy of the original variable (the new variable is no longer storage-associated with the original variable).

A threadprivate variable on the other hand will be most likely placed in the heap or in the thread local storage (that can be seen as a global memory local to a thread). A threadprivate variable persist across regions (depending on some restrictions). The master thread uses the original variable, all other threads make a private copy of the original variable (the master variable is still storage-associated with the original variable).

task 指令

可以指定某一task任务在指定第几个thread运行吗?

section 命令 与 for 命令的区别

简单理解sections其实是for的展开形式,适合于少量的“任务”,并且适合于没有迭代关系的“任务”。每一个section被一个线程去执行。

常用函数

omp_get_thread_num() //获取线程的num,即ID。在并行区域外,获取的是master线程的ID,即为0。
omp_get_num_threads/omp_set_num_threads()  //设置/获取线程数量,用于覆盖OMP_NUM_THREADS环境变量的设置。omp_set_num_threads在串行区域调用才会有效,omp_get_num_threads获取当前线程组的线程数量,一般在并行区域调用,在串行区域调用返回为1。
omp_get_max_threads() //返回OpenMP当前环境下能创建线程的最大数量。

环境变量

OMP_SCHEDULE:只能用到for,parallel for中。它的值就是处理器中循环的次数
OMP_NUM_THREADS:定义执行中最大的线程数
OMP_DYNAMIC:通过设定变量值TRUE或FALSE,来确定是否动态设定并行域执行的线程数
OMP_NESTED:确定是否可以并行嵌套

例子

#include <omp.h>

int main(int argc, _TCHAR* argv[])  
{
    printf("ID: %d, Max threads: %d, Num threads: %d \n",omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());
    omp_set_num_threads(5);
    printf("ID: %d, Max threads: %d, Num threads: %d \n",omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());

#pragma omp parallel num_threads(5)
    {
        // omp_set_num_threads(6);  // Do not call it in parallel region
        printf("ID: %d, Max threads: %d, Num threads: %d \n",omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());
    }

    printf("ID: %d, Max threads: %d, Num threads: %d \n",omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());

    omp_set_num_threads(6);
    printf("ID: %d, Max threads: %d, Num threads: %d \n",omp_get_thread_num(), omp_get_max_threads(), omp_get_num_threads());

    return 0;  
}

OpenMP和pthread是常见的模型

♦OpenMP为循环级并行提供了方便的功能。线程由编译器根据用户指令创建和管理。

♦pthread提供了更复杂、更动态的方法。线程由用户显式创建和管理。

需要进一步的研究学习

暂无

遇到的问题

暂无

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

对子句和制导的关系不清楚

参考文献

https://blog.csdn.net/gengshenghong/article/details/7004594

https://docs.microsoft.com/en-us/cpp/parallel/openmp/reference/openmp-clauses?view=msvc-160