Fortran中的矢量化和

时间:2015-08-27 18:48:33

标签: fortran sse gfortran simd avx

我正在使用Fortrangfortran编译我的-mavx代码并验证某些指令是通过objdump进行矢量化的,但我没有得到速度提升我期待,所以我想确保以下参数被矢量化(这条单指令约为运行时的50%)。

我知道某些指令可以被矢量化,而其他指令则不能,所以我想确保这可以是:

sum(A(i1:i2,ir))

同样,由于我在一个非常大的矩阵上执行此操作,因此这一行占用了大约50%的运行时间。我可以提供更多关于为什么我这样做的信息,但足以说它是必要的,尽管我可以在必要时重构内存(例如,我可以将总和作为{{1如果可以改为矢量化。

这条线是否被矢量化?我该怎么说?如果没有矢量化,我该如何强制进行矢量化?

编辑:感谢评论,我现在意识到我可以通过sum(A(ir,i1:i2))检查此求和的向量化,并看到这是向量化。我重新编写了如下代码:

-ftree-vectorizer-verbose
当我打开tsum = 0.0d0 tn = i2 - i1 + 1 tvec(1:tn) = A(i1:i2, ir) do ii = 1,tn tsum = tsum + tvec(ii) enddo 时,

ONLY 会进行矢量化,但我确实看到由于矢量化而增加了70%的速度。问题仍然存在:为什么-funsafe-math-optimizations没有矢量化,如何才能得到一个简单的sum(A(i1:i2,ir))来进行矢量化?

2 个答案:

答案 0 :(得分:1)

事实证明,除非我包含-ffast-math-funsafe-math-optimizations,否则我无法使用矢量化。

我玩的两段代码片段是:

tsum = 0.0d0
tvec(1:n) = A(i1:i2, ir)
do ii = 1,n
    tsum = tsum + tvec(ii)
enddo

tsum = sum(A(i1:i2,ir))

以下是运行带有不同编译选项的第一个代码段时的时间:

10.62 sec ... None
10.35 sec ... -mtune=native -mavx
 7.44 sec ... -mtune-native -mavx -ffast-math
 7.49 sec ... -mtune-native -mavx -funsafe-math-optimizations

最后,通过这些相同的优化,我能够将tsum = sum(A(i1:i2,ir))矢量化以获得

 7.96 sec ... None
 8.41 sec ... -mtune=native -mavx
 5.06 sec ... -mtune=native -mavx -ffast-math
 4.97 sec ... -mtune=native -mavx -funsafe-math-optimizations

当我们将sum-mtune=native -mavx-mtune=native -mavx -funsafe-math-optimizations进行比较时,它会显示约70%的加速比。 (请注意,这些只是每次运行一次 - 在我们发布之前,我们将在多次运行中进行真正的基准测试。)

我确实受到了轻微的打击。当我使用-f选项时,我的值略有变化。没有它们,我的变量(v1v2)的错误是:

v1 ... 5.60663e-15     9.71445e-17     1.05471e-15
v2 ... 5.11674e-14     1.79301e-14     2.58127e-15

但是通过优化,错误是:

v1 ... 7.11931e-15     5.39846e-15     3.33067e-16
v2 ... 1.97273e-13     6.98608e-14     2.17742e-14

表示确实存在不同的事情。

答案 1 :(得分:0)

您的显式循环版本仍然会以与矢量化版本不同的顺序添加FP。矢量版本使用4个累加器,每个累加器获得每个第4个数组元素。

您可以编写源代码以匹配矢量版本的功能:

tsum0 = 0.0d0
tsum1 = 0.0d0
tsum2 = 0.0d0
tsum3 = 0.0d0
tn = i2 - i1 + 1
tvec(1:tn) = A(i1:i2, ir)
do ii = 1,tn,4   ! count by 4
    tsum0 = tsum0 + tvec(ii)
    tsum1 = tsum1 + tvec(ii+1)
    tsum2 = tsum2 + tvec(ii+2)
    tsum3 = tsum3 + tvec(ii+3)
enddo

tsum = (tsum0 + tsum1) + (tsum2 + tsum3)

可能不使用-ffast-math进行矢量化。

FP add具有多周期延迟,但每个时钟吞吐量只有一个或两个,因此您需要使用asm来使用多个向量累加器来使FP添加单元饱和。 Skylake每个时钟可以增加两个FP,延迟= 4。以前的英特尔CPU每个时钟执行一次,延迟= 3。所以在Skylake上,你需要8个向量累加器才能使FP单元饱和。当然它们必须是256b向量,因为AVX指令速度快但是SSE向量指令的工作量是其两倍。

使用8 * 8累加器变量编写源代码将是荒谬的,所以我猜你需要-ffast-math,或者一个OpenMP pragma,告诉编译器不同的操作顺序是正常的。

显式展开您的源意味着您必须处理不是向量宽度*展开的倍数的循环计数。如果你对事物施加限制,它可以帮助编译器避免生成多个版本的循环或额外的循环设置/清理代码。