首页 > 解决方案 > 如何在 Fortran 中真正实现矢量化?

问题描述

以下所有代码都使用 -O3 -xHost(或 gfortran 中的 -Ofast -march=native)优化。

示例1

integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:)=a(:)+b(:)
return
end

上面的代码应该是矢量化的吧?real 是 real 8 类型,它应该在 ram 中占用 64 位。对于 AVX2,它带有 256 位寄存器或其他东西,上面的代码就像在一个时钟周期内做 c(1:4) = a(1:4) + b(1:4) 什么的,而 4 是因为 256/ 64=4。

但是为什么我经常发现实际执行循环更快?参见示例 2,

示例2

integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
integer :: i
do i=1,1000
    c(i)=a(i)+b(i)
endddo
return
end

我注意到 example1 中的编译器可能会抱怨数组未对齐或其他问题。因此实际上 example2 比 example1 快。但我的意思是原则上 example1 和 2 应该是相同的速度吧?在 example2 中,编译器应该足够聪明,可以相应地对代码进行矢量化。

最后,在下面的代码example3中,是否有任何向量化?

示例3

integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:) = exp(a(:)) + log(b(:))
return
end

或稍微复杂的事情,例如 example4 example4

integer, parameter :: r8=selected_real_kind(15,9)
subroutine f(a,b,c)
real(kind=r8) :: a(1000), b(1000), c(1000)
c(:) = exp(a(:)) + log(b(:)) + exp(a(:)) * log(b(:))
return
end

我的意思是矢量化仅适用于那些非常基本的操作,例如 op = + - * /

a(:) = b(:) op c(:)

但是,对于更复杂的事情,例如

 a(:) = log(b(:) + c(:)*exp(a(:))*ln(b(:)))

那么矢量化可能不起作用?

在许多情况下,使用 do 循环似乎比编写诸如 a(:)=b(:)+c(:) 之类的东西要快。似乎编译器在执行 do 循环时做得非常好或高度优化。

标签: fortranvectorizationsimdintel-fortran

解决方案


推荐阅读