我正在使用gfortran和-mavx编译我的gfortran代码,并且已经验证了一些指令是通过objdump向量化的,但是我并没有得到我所期望的速度改进,所以我想确保下面的参数是向量化的(这条指令大约是运行时的50%)。
我知道有些指令可以矢量化,而另一些指令则不能,所以我想确保:
sum(A(i1:i2,ir))
同样,这一行占用了运行时的大约50%,因为我是在一个非常大的矩阵上这样做的。我可以给出更多关于我为什么要这样做的信息,但只需说明这是必要的,尽管我可以在必要时重构内存(例如,如果可以将其向量化,我可以以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这个只在我打开-funsafe-math-optimizations时向矢量化,但是我确实看到了另一个70%的速度增长,因为矢量化。问题仍然存在:为什么sum(A(i1:i2,ir))不矢量化,我如何才能得到一个简单的sum来矢量化?
发布于 2015-08-28 15:48:34
事实证明,除非包括-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选项时,我的值略有变化。没有它们,我的变量(v1、v2)的错误是:
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这表明确实发生了一些不同的事情。
发布于 2015-08-28 23:07:35
您的显式循环版本仍然按照与矢量化版本不同的顺序添加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加法,用latency=4。以前的Intel CPU每个时钟做一个,用latency=3。所以在Skylake上,你需要8个矢量累加器来饱和FP单元。当然,它们必须是256 b矢量,因为AVX指令的速度是SSE矢量指令的两倍。
用8*8累加器变量编写源代码将是荒谬的,所以我想您需要-ffast-math,或者是一个告诉编译器不同操作顺序的OpenMP实用程序。
显式地展开源意味着必须处理不是向量宽度*展开倍数的循环计数。如果对事物施加限制,它可以帮助编译器避免生成多个版本的循环或额外的循环设置/清理代码。
https://stackoverflow.com/questions/32257244
复制相似问题