笔记¶
Rust
简介
Rust 速度惊人且内存利用率极高。由于没有运行时和垃圾回收,它能够胜任对性能要求特别高的服务,可以在嵌入式设备上运行,还能轻松和其他语言集成。
Rust 丰富的类型系统和所有权模型保证了内存安全和线程安全,让您在编译期就能够消除各种各样的错误。
安装
异常简单,默认安装在自己.local/bin
下,会自动修改bashrc/zshrc
On Linux and macOS systems, this is done as follows:
基础语法
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是如何实现内存安全的呢?
内存安全
- buffer overflow
- null pointer dereference
- use after free
- use of uninitialized memory
- illegal free (of an already-freed pointer, or a non-malloced pointer)
所有权
所有权对大多数开发者而言是一个新颖的概念,它是 Rust 语言为高效使用内存而设计的语法机制。所有权概念是为了让 Rust 在编译阶段更有效地分析内存资源的有用性以实现内存管理而诞生的概念。
所有权三规则
- Rust 中的每个值都有一个变量,称为其所有者。
- 一次只能有一个所有者。
- 当所有者不在程序运行范围时,该值将被删除。
原理
如果我们定义了一个变量并给它赋予一个值,这个变量的值存在于内存中。这种情况很普遍。但如果我们需要储存的数据长度不确定(比如用户输入的一串字符串),我们就无法在定义时明确数据长度,也就无法在编译阶段令程序分配固定长度的内存空间供数据储存使用。(有人说分配尽可能大的空间可以解决问题,但这个方法很不文明)。这就需要提供一种在程序运行时程序自己申请使用内存的机制——堆。本章所讲的所有"内存资源"都指的是堆所占用的内存空间。
有分配就有释放,程序不能一直占用某个内存资源。因此决定资源是否浪费的关键因素就是资源有没有及时的释放。
我们把字符串样例程序用 C 语言等价编写:
很显然,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
已有文件
- 未加密文件 secretfile.txt
- 接受方的公钥 recipients-key.pub
产生一次性加密解密对称密钥secret.key
256 bit (32 byte) random key
使用secret.key 加密所需文件
生成加密后文件secretfile.txt.enc
使用公钥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
<()
是子进程的意思。
发送加密文件
加密文件 secretfile.txt.enc和 secret.key.enc
use OpenSSL to descrypt file
已有文件
- 加密文件 secretfile.txt.enc和 secret.key.enc
- 接受方的私钥
解密secret.key.enc
解密secretfile.txt.enc
需要进一步的研究学习
- 使用 publickey.pem 可以看这篇 https://blog.csdn.net/makenothing/article/details/54645578
- 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
上面两张图组成一个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也略微减少。
游戏卡与专业卡的区别
- 应用方面不同
- 游戏卡会对三维图像处理有特殊处理,有光线追踪单元
- 专业计算卡,可能对某些格式的解压压缩有特殊单元,或者对半精度计算有特殊支持。
- 做工不同
- 专业卡由于在服务器上24小时不同工作,在多相供电,散热都堆料处理,游戏卡不同(公版,非公版肯定不一样)
- 驱动不同
- 游戏卡对应游戏软件的优化驱动,专业卡有对专业软件的驱动支持
- 价格不同
- 专业卡贵4倍不止。
- 参数的不同,对于同一颗核心(以RTX3090与A100 40G举例)
- A100的GA100是8块完整的,GA102是7块。
- A100领先的地方
1. 堆料完爆对手
2. 显存往往更多,AI应用
3. 访存更快
4. 支持 High bandwidth memory (HBM)
5. 在多精度和半精度有优势(NVIDIA A100 SXM4 40 GB VS.NVIDIA GeForce RTX 3090)
- RTX3090领先的地方 1. 频率更高 2. 有视频输出接口,支持OpenGL,DirectX 3. 有RT core 光追
参考文献
https://zhuanlan.zhihu.com/p/394352476
OpenMP
线程绑定
OpenMP 4.0 提供 OMP_PLACES
和 OMP_PROC_BIND
环境变量来指定程序中的 OpenMP 线程如何绑定到处理器。这两个环境变量通常结合使用。OMP_PLACES
用于指定线程将绑定到的计算机位置(硬件线程、核心或插槽)。OMP_PROC_BIND
用于指定绑定策略(线程关联性策略),这项策略指定如何将线程分配到位置。
除了 OMP_PLACES
和 OMP_PROC_BIND
这两个环境变量外,OpenMP 4.0 还提供可在 parallel 指令中使用的 proc_bind
子句。proc_bind
子句用于指定如何将执行并行区域的线程组绑定到处理器。
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;
编译制导格式
静态扩展 * 文本代码在一个编译制导语句之后,被封装到一个结构块中
孤立语句 * 一个OpenMP的编译制导语句不依赖于其它的语句
parallel
并行域中的代码被所有的线程执行
for
for语句指定紧随它的循环语句必须由线程组并行执行;
sections
sections编译制导语句指定内部的代码被划分给线程组中的各线程
不同的section由不同的线程执行
single
single编译制导语句指定内部代码只有线程组中的一个线程执行。
线程组中没有执行single语句的线程会一直等待代码块的结束,使用nowait子句除外
来自 https://ppc.cs.aalto.fi/ch3/nowait/
组合parallel for / parallel sections 编译制导语句
- Parallel for编译制导语句表明一个并行域包含一个独立的for语句
- parallel sections编译制导语句表明一个并行域包含单独的一个sections语句
同步结构
- master 制导语句
- 指定代码段只有主线程执行
- critical制导语句
- critical制导语句表明域中的代码一次只能执行一个线程,其他线程被阻塞在临界区
- 语句格式:
#pragma omp critical [name] newline
- barrier制导语句
- 同步一个线程组中所有的线程,先到达的线程在此阻塞,等待其他线程
- atomic制导语句
- 指定特定的存储单元将被原子更新
#pragma omp atomic x++;
- flush制导语句
- 标识一个同步点,用以确保所有的线程看到一致的存储器视图
- ordered制导语句
- 相对于critical,多了一个顺序
- 只能出现在for或者parallel for语句的动态范围中
- threadprivate语句使一个全局文件作用域的变量在并行域内变成每个线程私有
- 每个线程对该变量复制一份私有拷贝
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:
语句绑定与语句嵌套规则
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
VNC
常用命令
# shaojiemike @ node5 in ~ [11:26:56]
$ vncserver -list
TigerVNC server sessions:
X DISPLAY # RFB PORT # PROCESS ID
:1 5901 148718 (stale)
# shaojiemike @ node5 in ~ [11:29:39]
$ vncpasswd
Password:
# shaojiemike @ node5 in ~ [11:34:08]
$ vncserver -kill :1
Killing Xtigervnc process ID 148718... which was already dead
Cleaning stale pidfile '/home/shaojiemike/.vnc/node5:1.pid'!
# shaojiemike @ node5 in ~ [11:36:15]
$ vncserver
New 'node5:2 (shaojiemike)' desktop at :2 on machine node5
Starting applications specified in /etc/X11/Xvnc-session
Log file is /home/shaojiemike/.vnc/node5:2.log
Use xtigervncviewer -SecurityTypes VncAuth -passwd /home/shaojiemike/.vnc/passwd :2 to connect to the VNC server.
客户端转发
需要进一步的研究学习
暂无
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
无
RISC-V
RISC-V and ARM
RISC-V是完全开源的,虽然现在编译器和IDE生态还不行,但是在各国的大力推动下,未来可期。
相比於Arm架構,RISC-V的指令集更小、可以模組化擴充、客製化設計自由度等優點,經過數年發展,漸成為Arm架構的有力挑戰者。
RISC-V现在在物联网的应用是很好的。
The RISC-V Instruction Set Manual
多种不同长度的ISA
RV32I base integer instruction set.
RV32I contains 40 unique instructions.
For RV32I, the 32 x registers are each 32 bits wide,
Base Instruction Formats:
最先是符号位的原因是,立即数是二进制补码表示,也就是有负数的,所以有addi
指令但是没有subi
指令
为什么跳转的时候,最低位为0。为了支持RVC
application binary interface(ABI)
RV32E Base Integer Instruction Set( draft)
reduced version of RV32I designed for embedded systems. The only change is to reduce the number of integer registers to 16.
RV64I Base Integer Instruction Set
builds upon the RV32I variant。需要注意的一点,是访问的寄存器和寄存里的地址变成64位了,指令长度还是32位。
register: RV64I widens the integer registers and supported user address space to 64 bits
如果想要在RV64I里运行32位的指令,在指令后加后缀W就行。比如ADDIW
Additional instruction variants are provided to manipulate 32-bit values in RV64I, indicated by a ‘W’ suffix to the opcode.These “*W” instructions ignore the upper 32 bits of their inputs and always produce 32-bit signed values,
访存相关
The LD
instruction loads a 64-bit value from memory into register rd for RV64I.
The LW
instruction loads a 32-bit value from memory and sign-extends this to 64 bits before storing
it in register rd for RV64I. The LWU
instruction, on the other hand, zero-extends the 32-bit value
from memory for RV64I. LH
and LHU
are defined analogously for 16-bit values, as are LB
and
LBU
for 8-bit values. The SD
, SW
, SH
, and SB
instructions store 64-bit, 32-bit, 16-bit, and 8-bit
values from the low bits of register rs2 to memory respectively.
RV128I Base Integer Instruction Set
寄存器位数和地址空间变成128位。
Standard Extension for
- Integer Multiplication and Division
- MULDIV
- Atomic Instructions
- CSR Instructions
- Single-Precision Floating-Point
- Double-Precision Floating-Point
- Quad-Precision Floating-Point
- Bit Manipulation
- Vector Operations(draft)
指令速查
RISC-V assembly syntax
beq rs1, rs2, Label #RISC-V
SW rs2, imm(rs1) # Mem[rs1+imm]=rs2 ,汇编将访存放在最后
add rd, rs1, rs2 # rd = rs1 + rs2
Registers
frame pointer = BP in X86
ra = Link register in ARM
Some RISC-V compilers use a frame pointer, fp, or register x8 to point to first double word of frame.
指令设计
先用高维,预留低位来拓展
RVC (compressed)
32位时
minor 不包括func3 + opcode (去除固定2位)
major没func3
compiler explorer
编译器模拟器
https://godbolt.org/
array由于大小是一开始固定好的,分配在栈里。而vector是变的,分配在堆里?
RISC-V环境搭建
https://chivier.github.io/2022/02/04/2022/2202-QemuTest/
但是RVV向量化在git其他分支里,gcc7和8编译不会错
需要进一步的研究学习
Control and Status Registers (CSRs)
遇到的问题
暂无
开题缘由、总结、反思、吐槽~~
参考文献
无