学习
前世今生
SISD
远古时期,CPU是是SISD(单指令,单数据)
Such CPUs were also scalar and integer in nature – but what exactly does this mean? Scalar is where any math operation takes place on just single data values: it’s typically described as being SISD (single instruction, single data).
标量是什么意思呢?就是一个指令处理一条数据。比如,有一个32元素的数组a,然后你想给这个数组中的每个元素都加1,那怎么写呢,那就得发出32次加指令,然后去计算a[i] + 1。
for (int i = 0; i < 32; ++i)
a[i] += 1;
整数又是什么意思?字面意思,就是只能处理整数,以前要是想处理浮点数的话,需要另外的单独的处理器以及单独的指令集(x87)。这个历史过程是这样的:
最开始就是主板上需要安装两个处理器,一个专门处理整型,一个专门处理浮点型,叫协处理器
后来,intel 80486发布了,是第一款把那这俩东西集成到一起的芯片,处理浮点数的器件叫FPU,封装在一个芯片里就提高了运算速度嘛,通讯更快
再后来,intel推出了具有MMX技术的奔腾cpu
SIMD与MMX
MMX的出现改变了什么呢?能支持向量运算了,也就是支持SIMD(单指令,多数据)了。怎么支持的呢?上面提到的FPU中的寄存器是64位的,所以可以同时存放和计算2个32位/4个16位/8个8位,然后系统就可以在一条指令中同时处理这些内容了。(但是这时候功能还是有限的,第一,支持向量还是只适用于整数计算,第二,因为是使用了FPU中的寄存器进行计算的,所以使用MMX指令的时候不能同时进行浮点运算)
这块其实有个疑问来的,那当时的32位系统下,通用的32位寄存器理论上不也能实现SIMD嘛?32位寄存器理论上是可以同时支持2个16位/4个8位运行的吧?确实,我们手动操作可以实现这样的效果,但是比较麻烦,比如要在一个32位通用寄存器上进行2个16位加法,就需要通过移位之类的操作先把两个数放进一个寄存器中,然后运算完之后,还需要把两个数字提取分离出来。
那其实SIMD依赖的是什么呢?是人家的硬件支持加扩展的指令集。使用相关的指令可以直接进行SIMD的操作。
SSE
再后来啊,就又出现了SSE。SSE又有哪些改变呢?这一次它不再依赖FPU中的64位寄存器来实现向量运算了,而是单独扩展处理8个128位寄存器,还扩展了一些能处理浮点数的指令。
但这时候还有一些限制:SSE这时候还只支持32位的浮点数,4个32位浮点数,不支持整数运算;而且呢,这时候的SSE支持的128位寄存器虽然与FPU的寄存器分开了,但是SSE指令和FPU指令其实还是不能同时运行的,因为可能涉及到某些硬件资源的共享吧。
SSE2
后来又升级了SSE2,这时候支持的数据类型就更多了。four 32-bit or two 64-bit floats, as well as sixteen 8-bit, eight 16-bit, four 32-bit, or two 64-bit integers.
MMX寄存器还保留在cpu中,但是所有的MMX和SSE操作都可以使用单独的128位SSE寄存器进行存储。什么意思呢?就是其实128位的SSE寄存器也可以用来进行MMX的操作?
SSE3
The MMX registers remained in the processor, but all MMX and SSE operations could be made to place using the separate 128-bit SSE registers.
SSE4
这个版本中,寄存器没有修改,但是增加了更多指令能处理更多可能的数学运算之类的,也就是扩展了指令集
AVX(advanced vector extensions)
重大更新,把向量寄存器的数量扩展了一倍,大小扩展了一倍。之前SSE时代是8个128位寄存器,现在AVX时代已经扩展成了16个256个寄存器。
但这时候的数据格式方面还不如已经发展了很久的SSE4,现在AVX只能支持8个32位浮点/4个64位浮点
相对于SSE,AVX还有个改变,就是提供了一次处理三个值的能力:在SSE中,比如执行将两个数相加的操作,那相加的结果其实是放到存放这两个加数中的寄存器的其中一个上面的,也就是会把某个原始的加数覆盖。而AVX会保护原始值的安全,把运算结果存储到单独的寄存器中。(其实这块我有个疑问,不知道他这个指令集是怎么实现的,如果把结果存储到别的单独寄存器中,是不是需要对那个寄存器中的原始的值进行保存和恢复呢?这样的话是不是执行一条指令的时间和复杂度要比SSE高?)
Part of the problem of using AVX was that the load on the chip was so high that Intel had them automatically decrease the clocks in this mode, by around 20%, to keep the power consumption and heat levels down
这时候降频问题已经初现端倪了,因为寄存器位数要比一般的32位或者64位大很多,所以在运算时可能调用更多的晶体管,因此功耗也更大,耗电多,所以需要降频
AVX2和FMA
加了新的扩展,FMA(fused multiply-accumulate),但是注意这是一个独立于AVX的功能。
FMA是什么?允许将加法和乘法合并成一条指令,比如FMA指令可以在一条指令中发出如下操作:FMA instruction(a, b, c) = a * b + c,这个操作在一条指令中完成。
这块的实现上,intel和amd还有点不同呢:
INTEL:使用三个单独的操作数,可以是2个源操作数和一个答案,或者三个源操作数,然后将得到的答案覆盖其中一个源操作数所在的寄存器
AMD:使用了四个操作数,因此不会把答案覆盖掉其中一个存源操作数的寄存器
AVX512
寄存器数量和位宽再次加倍
And given that the use of AVX, in any form, results in the clocks being automatically decreased, the use of AVX-512 on such platforms would almost certainly be worse than using any of its predecessors, as it’s even more demanding of power when running.
功耗和降频问题也更凸显出来
峰值计算
按照AVX512来说,理论峰值的计算公式应该是:理论峰值 FLOPS=核心数×基频×32
核心数和基频容易理解,看看这个32是怎么来的?AVX512的寄存器宽度是512位,也就是同时能处理16个32为浮点数和8个64位浮点数,那人家计算肯定用多的计算,所以用16来算,一次处理16个32位浮点数,然后一个时钟周期是可以执行两条FMA指令,所以也就是一个时钟周期可以执行2*16个32位浮点数操作
AMD也搞了AVX512,但是在genoa上还不是完整的AVX512,他是使用2个256位来模拟的AVX512。所以其实他的峰值计算公式应该是理论峰值 FLOPS=核心数×基频×16,因为它一个时钟周期内只能完成16个浮点数操作
但是呢,也有个好处,就是他的降频可能不如完整的AVX512多,另外还有就是节省了CPU芯片上的一些空间。。。实在编不下去了
不过,人家从Turin开始也是支持完整的AVX512了,理论峰值的计算也就X32了,不过降频问题可能就更明显了
降频原因
其实我不太清楚的一点是,这个降频是怎么搞的?是因为识别到了执行AVX指令然后驱动进行调节的呢?还是说因为执行这个指令导致耗电上去了然后物理上的频率上不去了?
另外,这个降频是单个cpu下的?会不会影响其他cpu的频率?
看一下这篇论文:Analysis and Optimization ofDynamic Voltage and Frequency Scaling for AVX Workloads Using a Software-Based Reimplementation,正好也能加深学习一下频率方面的知识
为AVX AVX512这些feature设计的unit需要在芯片中特别小的空间里完成相对普通的32位64位寄存器更多的位宽的处理,所以硬件上会增加很多数据处理路径之类的东西,那在执行这种指令时就需要降频处理以保证其稳定性,要保证功耗保持在在TDP(Thermal Design Power)限制内。而在执行AVX代码时降频就意味着在同一个core上执行的其他指令也会降频,而且,当前的intel cpu就是在执行完最后一条AVX指令后也不会马上恢复频率,而是保持这个频率运行一些时间(类似halt poll这种搞一个一个窗口期的感觉?防止下一条AVX指令过来时还得重新降频处理?),有人使用nginx实验时,AVX512的吞吐甚至还不如SSE4.
Now, the performance reduction observed when enabling AVX originates from the fact that the other part of the program is slowed down due to the attained frequency reduction after AVX instructions were executed。所以,拖累性能的原因是什么呢?不是因为使用AVX触发了降频?而是因为触发了降频的CPU还要去执行其他的非AVX指令。所以,可想而知如果某个程序大部分都是avx指令的话性能增长应该还是
在Intel上支持了HWP之后,就不再内核层面来调节频率了,而是通过硬件本身检测负载调节频率。
有些大佬的做法是把线程和core分成avx和非avx的,然后分开处理指令。但是这样我感觉有个问题呀,非avx的core上,那些寄存器岂不是就用不到了
另外,降频是core粒度的还是什么粒度的,会不会影响其他cpu呢?从文档的只言片语中推测可能是单个core上的?因为说是avx所在的core降频会影响该core上其他非avx指令,没说其他core的情况。
看一下intel的官方文档,如下图所示,可见一斑,在icelake删是有三个freq 级别的。当运行普通的标量指令、AVX、SSE和轻量的AVX2肯定就最大全核睿频,heavy AVX2或者light AVX 512全核睿频频率就下降一个等级,运行heavy AVX512全核睿频频率就再降一级。
所以这些是硬件自动调节的?因为现在的cpu都支持HWP了,所以pstate驱动中所做的事情就不多了,调频这些事都由硬件来管了
当然,如果想让驱动来管理调频也不是没办法,cmdline中加上intel_pstat=no_hwp就行了,这个大佬的论文中就需要用,因为他需要修改intel_pstate的驱动,在驱动层调控cpu频率,,再后面的先不看了就,完成工作要紧
监控
那有办法监控到上面说的降频嘛?有三种freq level
perf list |grep -i avx能看到有相关的event
core_power.lvl0_turbo_license
[Core cycles where the core was running in a manner where Turbo may be
clipped to the Non-AVX turbo schedule]
core_power.lvl1_turbo_license
[Core cycles where the core was running in a manner where Turbo may be
clipped to the AVX2 turbo schedule]
core_power.lvl2_turbo_license
[Core cycles where the core was running in a manner where Turbo may be
clipped to the AVX512 turbo schedule]
没看到amd的相关文档,
测试
intel上测试一下看看能不能观察到level license呢?步骤如下:
- 把机器上的gcc升级一下,换个新点的,资料说是6.0以上
- 让deepseek帮忙写了个使用avx512的程序
主要就是使用immintrin库来支持avx操作
#include <stdio.h>
#include <immintrin.h>
#include <stdlib.h>
#include <time.h>
#define ARRAY_SIZE 1024 * 1024 * 16
int main() {
float *a = (float *)_mm_malloc(ARRAY_SIZE * sizeof(float), 64);
float *b = (float *)_mm_malloc(ARRAY_SIZE * sizeof(float), 64);
float *c = (float *)_mm_malloc(ARRAY_SIZE * sizeof(float), 64);
for (int i = 0; i < ARRAY_SIZE; i++) {
a[i] = (float)rand() / RAND_MAX;
b[i] = (float)rand() / RAND_MAX;
}
clock_t start = clock();
int num_iterations = 1000;
for (int iter = 0; iter < num_iterations; iter++) {
for (int i = 0; i < ARRAY_SIZE; i += 16) {
__m512 va = _mm512_load_ps(&a[i]);
__m512 vb = _mm512_load_ps(&b[i]);
__m512 vc = _mm512_add_ps(_mm512_mul_ps(va, vb), _mm512_set1_ps(1.0f));
_mm512_store_ps(&c[i], vc);
}
}
clock_t end = clock();
double elapsed_time = (double)(end - start) / CLOCKS_PER_SEC;
printf("AVX-512 计算完成!\n");
printf("耗时: %.6f 秒\n", elapsed_time);
_mm_free(a);
_mm_free(b);
_mm_free(c);
return 0;
}
- 编译程序
要加上-mavx512f启用avx512指令集
gcc -O3 -mavx512f -o a a.c
- 检测freq levels
使用以下命令
perf stat -e core_power.lvl2_turbo_license,core_power.lvl1_turbo_license,core_power.lvl0_turbo_license
当我不运行上面的程序时结果如下
Performance counter stats for 'system wide':
0 core_power.lvl2_turbo_license
0 core_power.lvl1_turbo_license
2,420,631,212 core_power.lvl0_turbo_license
3.318109819 seconds time elapsed
当我运行上面的程序再抓perf时,结果如下:可以看到,intel上执行avx512的时候,确实会触发降频
Performance counter stats for 'system wide':
18,176,437,873 core_power.lvl2_turbo_license
0 core_power.lvl1_turbo_license
2,076,976,157 core_power.lvl0_turbo_license
3.255332255 seconds time elapsed
- 检测频率
还是有那个疑问,这个影响是core粒度的嘛?
当前我的机器是全核睿频3.1Ghz,在什么都不运行的情况下,cpupower monitor看到全部核心的频率都维持在3.1Ghz左右
然后我绑核测试taskset -c 127 ./a,截取部分结果如下,127和63号cpu都降频了,这个正常因为是一个core上的两个HT。但同时还发现一个现象,其他的cpu上好像有一部分的频率超过了3.1Ghz,甚至有几个睿频到4Ghz以上,即使是另一个numa上。这能说明什么呢?avx降频确实是core粒度的?但是会牵连影响到其他cpu?一个core上频率下降了其他core上频率会出现或多或少的提升?这就应该不是和avx相关的了吧,是睿频调节策略导致的?
1| 22| 118| 0.00| 0.00| 0.00| 0.00|| 0.02| 99.98| 3213|| 0.00| 99.95
1| 23| 55| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3631|| 0.00| 100.0
1| 23| 119| 0.00| 0.00| 0.00| 0.00|| 0.67| 99.33| 3091|| 0.00| 99.42
1| 24| 56| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3431|| 0.00| 100.0
1| 24| 120| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3219|| 0.00|100.00
1| 25| 57| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3196|| 0.00| 100.0
1| 25| 121| 0.00| 0.00| 0.00| 0.00|| 0.15| 99.85| 3092|| 0.00| 99.77
1| 26| 58| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3545|| 0.00| 100.0
1| 26| 122| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3119|| 0.00| 100.1
1| 27| 59| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3584|| 0.00| 100.0
1| 27| 123| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3132|| 0.00|100.00
1| 28| 60| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3442|| 0.00| 100.0
1| 28| 124| 0.00| 0.00| 0.00| 0.00|| 0.00|100.00| 3161|| 0.00|100.00
1| 29| 61| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3357|| 0.00| 100.0
1| 29| 125| 0.00| 0.00| 0.00| 0.00|| 0.03| 99.97| 3097|| 0.00| 99.98
1| 30| 62| 0.00| 0.00| 0.00| 0.00|| 0.01| 99.99| 3231|| 0.00| 100.0
1| 30| 126| 0.00| 0.00| 0.00| 0.00|| 0.28| 99.72| 3093|| 0.00| 99.82
1| 31| 63| 0.00| 0.00| 0.00| 0.00|| 1.04| 98.96| 2805|| 0.00| 99.01
1| 31| 127| 0.00| 0.00| 0.00| 0.00|| 99.78| 0.22| 2794|| 0.00| 0.00
参考
https://www.techspot.com/article/2166-mmx-sse-avx-explained/
https://zhuanlan.zhihu.com/p/678584638
https://blog.cloudflare.com/on-the-dangers-of-intels-frequency-scaling/
论文:Analysis and Optimization ofDynamic Voltage and Frequency Scaling for AVX Workloads Using a Software-Based Reimplementation(https://link.zhihu.com/?target=https%3A//os.itec.kit.edu/downloads/2019_BA_Khalil_AVX_DFVS_Software_Reimplementation.pdf)
https://travisdowns.github.io/blog/2020/01/17/avxfreq1.html
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 857879363@qq.com