24

我有一个具有 AVX2 和 FMA3 的 i5-4250U。我正在我编写的 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应该允许一个宽松的浮点模型如何在 SSE/AVX 中使用融合乘加 (FMA) 指令

编辑:

根据 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 快了很多)。

4

2 回答 2

9

在这里只回答问题的一小部分。如果你写_mm256_add_ps(_mm256_mul_ps(areg0,breg0), tmp0),gcc-4.9 几乎像内联汇编一样处理它,并没有对其进行太多优化。如果将其替换areg0*breg0+tmp0为 gcc 和 clang 都支持的语法,则 gcc 开始优化并可能使用 FMA(如果可用)。我对 gcc-5进行了改进,_mm256_add_ps例如现在实现为简单地使用的内联函数+,因此也可以优化具有内在函数的代码。

于 2014-02-08T19:37:49.710 回答
5

以下编译器选项现在足以收缩_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,但它仍然没有收缩(惊喜)。 虽然 MSVC 将收缩标量操作

GCC 至少从 GCC 5.1 开始这样做。


尽管对于某些编译器的这种优化-O1已经足够了,但始终至少-O2用于整体性能,最好-O3 -march=native -flto也是配置文件引导的优化。

如果你的代码没问题,-ffast-math.

于 2015-12-25T10:12:38.727 回答