本文记录在尝试测试AMD上某个AVX2 benchmark过程中遇到的问题和一些值得吐槽的点。跑这个测试的一个主要的目标是,将支持AVX512的kernel降级成支持AVX2的,然后在AMD平台上测试性能。
Intel Intrinsics
AVX是Intel设计并推行的多媒体SIMD指令集,同时提供了一系列内建函数可以供给高级语言(C/C++)使用。
这个doc可以很方便的查询内建函数的使用API和对应函数涉及到的指令Intel® Intrinsics Guide。
但是从目前的情况来看,使用内建函数意味着完全依靠编译器来进行SIMD指令优化,效果和控制程度是肯定不如直接写汇编的。未来可能有时间和机会的话还是要熟悉一下SIMD汇编指令的coding和benchmarking。
另外,这里值得吐槽的一点是,AVX2内建函数只有gather而没有scatter,后者需要手动去做,从AVX512才开始加入scatter的内建函数。还有就是,AVX2以及之前SSE的gather函数的参数接口都是一样的,到了AVX512,把头两个参数的顺序交换了!交换了!不明白意义在哪...
Chrono Library
在跑测试程序的时候,主要的测试方式是在程序内部使用time相关的库对kernel的执行时间进行测试。
最开始的AVX512程序使用的是chrono这个库,于是修改后的版本也沿用这个版本的测试方法。但是在实际编译(-static -O2)之后,当注释掉kernel的调用时,依然有10ms的结果报出,很是奇怪。在记录start和end部分与计算end-start部分之间加入多一行的代码(比如printf),就恢复正常的0ms。
这个问题在现在无法复现了...
OpenMP调度策略
程序使用OpenMP+SIMD Vector的方式,能够完全利用上现代CPU的并行能力。
OpenMP在使用上可以人为指定调度的策略,比如static、dynamic、guided之类。在负载不均衡的情况下,可以使用dynamic调度策略,以尽量减少长板效应带来的负面影响,但是代价就是动态迁移线程带来的开销。
在测试程序的时候,使用dynamic调度的效果并不理想,远远不如static调度,查找问题忙活了半天,最后发现OpenMP的库是使用Intel 2019年版的,使用之后的版本就不存在这样的问题了。
这个在之后有时间可以去实际测试折腾一下。
GCC -g -O2
GCC的-g和-O2是可以一起给出来的,只不过-g里包含的部分symbol可能会被O2优化掉从而找不到。GCC提供了-Og的方案,以解决这个问题。
在测试程序的时候,需要反汇编查看具体的机器指令,确定kernel某些部分没有被优化掉,这部分我没有带上-O2只有-g导致误以为没有被优化,从而浪费了许多实验的时间。
AVX浮点峰值算力
AVX2版本的程序在AMD EYPC平台上跑出来的性能不如CSR串行程序。在这里卡了很久,不知道是什么原因,可能是平台的问题,也可能是我改AVX512改成AVX2的技能还不到家,造成性能损失。
现在看来,一个正确的分析流程应该是:
- 首先测量CPU系统上AVX的浮点峰值性能(cpufp,依靠重复堆叠fmadd指令),以及标量的浮点峰值性能。
- 其次使用perf/vtune做一个全面的系统测试,统计各个部分的pipline占用。
- 最后根据之前的两个结果,再次修改Kernel。
此外,使用C/C++和内建函数,对于热点部分的计算,可控性还是差一些,理想的情况应该是直接写Assembly吧~
Epyc or Ryzen?
虽然最后更换平台在Ryzen 5950X上修改后的效果达到理想了,但是真的能把之前AVX的效果不如Scalar归罪于Epyc 7401P吗?可能还需要更进一步的测试实验吧!