6

我有一些用 numpy 编写的代码,我正在考虑将其移植到 Fortran 以获得更好的性能。

我多次执行的一项操作是将两个数组的元素乘积相加:

sum(A*B)

看起来融合乘加指令会对此有所帮助。我目前的处理器不支持这些指令,所以我还不能测试。但是,我可能会升级到支持 FMA3(Intel Haswell 处理器)的新处理器。

有谁知道用“-march=native”(或等效的 ifort)编译程序是否足以让编译器(gfortran 或 ifort)明智地使用 SIMD 指令来优化该代码,或者你认为我会必须婴儿编译器或代码?

4

3 回答 3

3

如果您-march=native在带有 SIMD 的机器上使用,编译器应该生成 SIMD 指令,尽管我一直使用-xHostflag 而不是 ifort。

但我不太确定如何让他们“明智地”做到这一点。我的感觉是,在-O3ifort 和 gfortran 级别上,它们都倾向于在矢量化方面过于激进(也就是说,它们使用 SIMD 功能的频率超过了应有的程度)。很多时候我必须关闭矢量化以获得最有效的代码。当然,这对您来说可能是真的,也可能不是。

使用为此任务优化的向量库通常会更好。您可以vdmul在 MKL 或GS​​L 中使用gsl_vector_mul来执行此操作。

使用-march=NEWARCH将导致针对架构 NEWARCH 调整的代码,但不能在早期架构上运行。您可以使用-mtune=NEWARCHNEWARCH 是新处理器架构的标志。这将生成针对新架构调整的代码,但仍可在旧架构上执行。由于您还没有新机器,-mtune可能是您目前需要的。

使用 ifort,您可以使用矢量化报告标志来显示程序的哪个部分已被矢量化。例如,ifortflag-vec-report=1会在编译期间为您提供此类信息。我确信在 gfortran 中会有一个等效的标志。

于 2014-01-11T17:53:14.427 回答
2

gfortran 版本sum(a*b)提供了比dot_product(a,b)长期过时更好的矢量化。您显示的代码使用串行 AVX2 fma 指令。

dot_product没有间接索引或其他复杂情况(一个简单的循环本身)的实现中,fma 可能会比 simd 并行乘法和加法指令的组合慢,因为乘法可以在延迟关键路径之外完成。gfortran 对 dot_product 使用并行 simd fma 在更复杂的情况下可能非常有效。

您将需要任何一个-O2 -ftree-vectorize -ffast-math -march=native or -O3 -ffast-math -march=native(以及合适的向量长度)来对其进行向量化,并且 gfortran 可能无法在 OpenMP 并行区域内进行向量化。

gfortran 4.9 似乎删除了选项 -ftree-vectorizer-verbose。-fdump-tree-vect 将矢量化传递的详细信息写入 .vect 文件,为不同的主要 gcc 版本选择不同的名称。

于 2014-01-28T17:37:10.547 回答
1

感谢朱小雷的提示,我现在知道 gfortran 将使用 fused multiply-add 来优化sum(A*B). 例如,使用以下代码:

程序测试隐式无

真实的,维度(7)::a,b

a = (/ 2.0, 3.0, 5.0, 7.0, 11.0, 13.0, 17.0 /)

b = (/ 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0 /)

打印 *, sum(a*b)
结束程序

我可以用f95 sum.f95 -o sum -O3 -march=core-avx2, 编译它并objdump -d sum | grep vfmadd显示

40088b: c4 e2 71 99 44 24 30 vfmadd132ss 0x30(%rsp),%xmm1,%xmm0

400892: c4 e2 69 b9 44 24 34 vfmadd231ss 0x34(%rsp),%xmm2,%xmm0

400899: c4 e2 61 b9 44 24 38 vfmadd231ss 0x38(%rsp),%xmm3,%xmm0

4008a0: c4 e2 59 b9 44 24 3c vfmadd231ss 0x3c(%rsp),%xmm4,%xmm0

4008a7: c4 e2 51 b9 44 24 40 vfmadd231ss 0x40(%rsp),%xmm5,%xmm0

4008ae: c4 e2 49 b9 44 24 44 vfmadd231ss 0x44(%rsp),%xmm6,%xmm0

4008b5: c4 e2 41 b9 44 24 48 vfmadd231ss 0x48(%rsp),%xmm7,%xmm0

所以 gfortran 展开循环并放入 7 条融合乘加指令。如果我创建更大的随机多维数组,我仍然会看到 vfmadd231ss 弹出一次(因此它不会展开循环)。

于 2014-01-11T21:47:56.093 回答