我有一个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 - &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.因为Ivy Bridge可以同时进行一次AVX乘法和一次AVX添加(因此我已经多次展开循环以确保这一点),因此ILP为2.
我只有大约55%的峰值失误(使用FMA).我不确定为什么,但至少我现在看到的东西.
一个副作用是,当我比较一个我知道我信任的简单矩阵乘法算法时,我现在得到一个小错误.我认为这是因为FMA只有一种舍入模式,而不是通常的两种舍入模式(具有讽刺意味的是,它违反了IEEE浮点规则,即使它可能更好).
编辑:
有人需要重做 如何实现每个周期4个FLOP的理论最大值? 但Haswell每循环做8个双浮点FLOPS.
编辑
实际上,Mysticial更新了他的项目以支持FMA3(参见上面链接中的回答).我在Windows8中使用MSVC2012运行他的代码(因为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
例如现在实现为简单使用的内联函数+
,因此具有内在函数的代码也可以进行优化.