【发布时间】:2014-01-26 21:24:47
【问题描述】:
我有一个 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,我得到了超过 80% 的 CPU 峰值触发器,但我认为我应该能够用 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 应该允许一个宽松的浮点模型 How to use Fused Multiply-Add (FMA) instructions with SSE/AVX
编辑:
基于 Mysticial 的 cmets,我继续使用 _mm256_fmadd_ps,现在 AVX2+FMA 版本更快。 我不确定为什么编译器不会为我执行此操作。我现在在超过 1000x1000 的矩阵中获得了大约 80 GFLOPS(没有 FMA 的峰值 flops 的 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 浮点规则,尽管它可能更好)。
编辑:
有人需要重做 How do I achieve the theoretical maximum of 4 FLOPs per cycle? 但是使用 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 快了很多)。
【问题讨论】:
-
检查程序集以查看编译器是否实际使用 FMA 指令。如果不是,并且您已经尝试了一堆编译器选项(包括
-ffast-math),那么您唯一的选择是手动使用内在函数。 (或者只使用使用它的库)换句话说,即使您指定-mfma和-ffast-math,编译器也可能不够智能以生成 FMA。 -
我接受了你的建议@Mysticial 并使用了
_mm256_fmadd_ps,现在我得到了超过 110% 的峰值失败率(在没有 FMA 的情况下计算)。我在我的 15 瓦小 CPU(在英特尔 NUC 中)上获得了超过 80 GFLOPS。但是当我与我的基本计算进行比较时,我得到了一些小错误。我认为这是由于 FMA 的新舍入模式。我将不得不改变我的比较。不知道该怎么做。另一方面,我并没有得到我预期的那么大的提升(我之前获得了 80% 的峰值失败率)。 -
这是非常典型的优化。当你让事情变得更快时,你就会暴露出新的瓶颈。所以你可能不再受计算的束缚。您是否正在最大化缓存带宽?您的负载存储吞吐量?
-
@Zboson:是的,使用 FMA 会稍微扰乱结果(平铺和矢量化以及进入快速矩阵相乘的大多数其他技巧也会如此)。通常,通过确保某个矩阵范数中的相对误差受矩阵大小线性阈值的限制来测试这些操作。
-
@Zboson 对于您的第二次编辑,单击我从 Flops 问题链接的 github 项目。我最近更新了它以包含 FMA3。