我有一个i5-4250U,它有AVX2和FMA3。我正在Linux上测试GCC 4.8.1中的一些密集矩阵乘法代码,这是我写的。下面列出了我编译的三种不同方法。
SSE2: gcc matrix.cpp -o matrix_gcc -O3 -msse2 -fopenmp
AVX: gcc matrix.cpp -o matrix_gcc -O3 -mavx -fopenmp
AVX2+FMA: gcc matrix.cpp -o matrix_gcc -O3 -march=native -fopenmp -ffast-math
SSE2和AVX版本在性能上明显不同。然而,AVX2+FMA并不比AVX版本更好。我不明白。假设没有FMA,我会得到CPU 80%以上的峰值失败,但我认为我应该能够用FMA做得更好。矩阵乘法应直接受益于FMA。我基本上是在AVX中同时做八点产品。当我检查march=native
时,它给出:
cc -march=native -E -v - </dev/null 2>&1 | grep cc1 | grep fma
...-march=core-avx2 -mavx -mavx2 -mfma -mno-fma4 -msse4.2 -msse4.1 ...
所以我可以看到它被启用了(只是为了确保我添加了-mfma
,但它没有什么区别)。ffast-math
应该允许一个宽松的浮点模型如何将融合乘加(FMA)指令与SSE/AVX 一起使用
编辑:
根据Mysticial的评论,我继续使用_mm256_fmadd_ps,现在AVX2+FMA版本更快了我不知道编译器为什么不帮我做这件事。对于1000x1000多个矩阵,我现在得到了大约80 GFLOPS(没有FMA的峰值触发器的110%)。如果有人不相信我的峰值失败计算,这里就是我所做的。
peak flops (no FMA) = frequency * simd_width * ILP * cores
= 2.3GHZ * 8 * 2 * 2 = 73.2 GFLOPS
peak flops (with FMA) = 2 * peak flops (no FMA) = 146.2 GFLOPS
当使用两个核心时,我的CPU在turbo模式下是2.3 GHz。ILP得到2,因为Ivy Bridge可以同时进行一次AVX乘法和一次AVX加法(为了确保这一点,我已经多次展开循环)。
我只得到了大约55%的峰值失败(FMA)。我不知道为什么,但至少我现在看到了一些东西。
一个副作用是,当我与我信任的简单矩阵乘法算法进行比较时,我现在会出现一个小错误。我认为这是因为FMA只有一种舍入模式,而不是通常的两种(具有讽刺意味的是,这打破了IEEE浮点规则,尽管它可能更好)。
编辑:
有人需要重做如何实现每个循环理论上最多4个FLOP?但是用Haswell每循环进行8次双浮点FLOPS。
编辑
事实上,Mysticial已经更新了他的项目来支持FMA3(见上面链接中的答案)。我用MSVC2012在Windows8中运行了他的代码(因为Linux版本没有使用FMA支持进行编译)。以下是结果。
Testing AVX Mul + Add:
Seconds = 22.7417
FP Ops = 768000000000
FLOPs = 3.37705e+010
sum = 17.8122
Testing FMA3 FMA:
Seconds = 22.1389
FP Ops = 1536000000000
FLOPs = 6.938e+010
sum = 333.309
这是双浮点FMA3的69.38 GFLOPS。对于单个浮点,我需要将其加倍,所以这是138.76 SP GFLOPS。我计算我的峰值是146.2 SP GFLOPS这是峰值的95%换句话说,我应该能够大大改进我的GEMM代码(尽管它已经比Eigen快了一点)。
这里只回答了问题的一小部分。如果您编写_mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0)
,gcc-4.9会像内联asm一样处理它,并且不会对它进行太多优化。如果将其替换为areg0*breg0+tmp0
,这是一种gcc和clang都支持的语法,那么gcc将开始优化,并可能使用FMA(如果可用)。我改进了对于gcc-5,例如_mm256_add_ps
现在被实现为一个只使用+
的内联函数,因此也可以优化具有内部函数的代码。
以下编译器选项足以将_mm256_add_ps(_mm256_mul_ps(a, b), c)
压缩为单个fma指令(例如vfmadd213ps
):
GCC 5.3: -O2 -mavx2 -mfma
Clang 3.7: -O1 -mavx2 -mfma -ffp-contract=fast
ICC 13: -O1 -march=core-avx2
我在微软风投公司试用了/O2 /arch:AVX2 /fp:fast
,但它仍然没有收缩(惊喜)。不过MSVC将收缩标量运算。
GCC至少从GCC 5.1开始这样做。
尽管-O1
对于某些编译器的此优化来说是足够的,但始终至少使用-O2
来实现总体性能,最好是-O3 -march=native -flto
,以及配置文件导向优化。
如果你的代码可以的话,-ffast-math
。