Fortran 循环矢量化给出了不同的答案

Fortran 循环矢量化给出了不同的答案,fortran,vectorization,gfortran,Fortran,Vectorization,Gfortran,我正在构建一些单元测试,发现我的代码在矢量化时给出的结果略有不同。在下面的示例中,数组a在一维中求和,并添加到初始值x。a的大多数元素太小,无法更改x。代码是: module datamod use ISO_FORTRAN_ENV, only : dp => REAL64 implicit none ! -- Array dimensions are large enough for gfortran to vectorize integer, parameter

我正在构建一些单元测试,发现我的代码在矢量化时给出的结果略有不同。在下面的示例中,数组
a
在一维中求和,并添加到初始值
x
a
的大多数元素太小,无法更改
x
。代码是:

module datamod
   use ISO_FORTRAN_ENV, only : dp => REAL64
   implicit none

   ! -- Array dimensions are large enough for gfortran to vectorize
   integer, parameter :: N = 6
   integer, parameter :: M = 10
   real(dp) :: x(N), a(N,M)

contains
subroutine init_ax
   ! -- Set a and x so the issue manifests

   x = 0.
   x(1) =  0.1e+03_dp

   a = 0.
   ! -- Each negative component is too small to individually change x(1)
   ! -- But the positive component is just big enough
   a(   1,   1) =  -0.4e-14_dp
   a(   1,   2) =  -0.4e-14_dp
   a(   1,   3) =  -0.4e-14_dp
   a(   1,   4) =   0.8e-14_dp
   a(   1,   5) =  -0.4e-14_dp
end subroutine init_ax
end module datamod

program main
   use datamod, only : a, x, N, M, init_ax
   implicit none
   integer :: i, j

   call init_ax

   ! -- The loop in question
   do i=1,N
      do j=1,M
         x(i) = x(i) + a(i,j)
      enddo
   enddo

   write(*,'(a,e26.18)') 'x(1) is: ', x(1)
end program main
该代码在gfortran中给出了以下结果,其中不包含循环矢量化,也包含循环矢量化。请注意,
ftree-vectorize
包含在
-O3
中,因此使用
-O3
时问题也会显现出来

mach5% gfortran -O2 main.f90 && ./a.out
x(1) is:   0.100000000000000014E+03
mach5% gfortran -O2 -ftree-vectorize main.f90 && ./a.out
x(1) is:   0.999999999999999858E+02
我知道某些编译器选项可以更改答案,例如
-fassocialativemath
。但是,根据gcc页面,标准的
-O3
优化包中不包括这些内容

在我看来,矢量化的代码似乎是先将
a
的所有组件相加,然后再加上x。但是,这是不正确的,因为编写的代码要求将
a
的每个组件添加到
x


这是怎么回事?在某些情况下,循环矢量化可能会改变答案吗?Gfortran版本4.7和5.3也有同样的问题,但Intel 16.0和PGI 15.10没有。

我将您提供的代码复制到一个名为test.f90的文件中,然后使用Gfortran版本4.8.5编译并运行它。我发现
-O2
-O2-ftree-vectorize
选项的结果会随着结果的不同而不同。然而,当我简单地使用
-O3
时,我发现结果与
-O2
匹配

$ gfortran --version
GNU Fortran (GCC) 4.8.5 20150623 (Red Hat 4.8.5-11)
Copyright (C) 2015 Free Software Foundation, Inc.

GNU Fortran comes with NO WARRANTY, to the extent permitted by law.
You may redistribute copies of GNU Fortran
under the terms of the GNU General Public License.
For more information about these matters, see the file named COPYING

$ gfortran -O2 test.f90 && ./a.out
x(1) is:   0.100000000000000014E+03
$ gfortran -O2 -ftree-vectorize test.f90 && ./a.out
x(1) is:   0.999999999999999858E+02
$ gfortran -O3 test.f90 && ./a.out
x(1) is:   0.100000000000000014E+03

我不太确定我在这里看到了你的问题。绝大多数编译器优化都是对指令重新排序,以便更好地适应机器,同时保持程序在数学上(但不一定是数字上)等效。所以,仅仅因为你是按那个顺序写的,并不意味着计算机必须按那个顺序执行。语言允许这样做,为什么你不希望这样呢?我认为
gfortran
-O3
的情况不应该是这样的。根据我在问题中包含的链接,所有控制浮点运算行为的编译器选项都必须特别启用。如果我错了,并且所有的优化都可以做到这一点,那么我会对
gfortran
的一个选项感兴趣,就像英特尔的
-fp model precise
,它要求优化不会改变最终结果。