我正在考虑编写SIMD向量数学库,因此,作为一个快速的基准测试,我编写了一个程序,该程序执行1亿个(按4个浮点数)矢量逐元素乘法,并将它们相加为累加总数。对于我的经典非SIMD变体,我只是制作了一个具有4个浮点数的结构,并编写了自己的乘法函数“ multiplyTwo”,该函数将两个这样的结构元素明智地相乘并返回另一个结构。对于我的SIMD版本,我使用了“ immintrin.h”以及__m128,_mm_set_ps和_mm_mul_ps。我正在i7-8565U处理器(威士忌湖)上运行,并使用以下命令编译:g++ main.cpp -mavx -o test.exe
以在GCC中启用AVX扩展指令。
奇怪的是,SIMD版本大约需要1.4秒,而非SIMD版本只需要1秒。我觉得好像做错了什么,因为我认为SIMD版本的运行速度应该快4倍。感谢您的帮助,代码如下。我已经在注释中放置了非SIMD代码,当前形式的代码是SIMD版本。
#include "immintrin.h" // for AVX
#include <iostream>
struct NonSIMDVec {
float x, y, z, w;
};
NonSIMDVec multiplyTwo(const NonSIMDVec& a, const NonSIMDVec& b);
int main() {
union { __m128 result; float res[4]; };
// union { NonSIMDVec result; float res[4]; };
float total = 0;
for(unsigned i = 0; i < 100000000; ++i) {
__m128 a4 = _mm_set_ps(0.0000002f, 1.23f, 2.0f, (float)i);
__m128 b4 = _mm_set_ps((float)i, 1.3f, 2.0f, 0.000001f);
// NonSIMDVec a4 = {0.0000002f, 1.23f, 2.0f, (float)i};
// NonSIMDVec b4 = {(float)i, 1.3f, 2.0f, 0.000001f};
result = _mm_mul_ps(a4, b4);
// result = multiplyTwo(a4, b4);
total += res[0];
total += res[1];
total += res[2];
total += res[3];
}
std::cout << total << '\n';
}
NonSIMDVec multiplyTwo(const NonSIMDVec& a, const NonSIMDVec& b)
{ return {a.x*b.x + a.y*b.y + a.z*b.z + a.w*b.w}; }
禁用优化功能(gcc的默认值为-O0
),内在函数通常很糟糕。内在函数的Anti-optimized -O0
code-gen通常会带来很多伤害(甚至比标量更大),并且某些类似于函数的内在函数会带来额外的存储/重载开销。加上-O0
的额外存储转发延迟往往会受到更大的损害,因为使用1个矢量而不是4个标量执行操作时,ILP会更少。
使用-O0