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

时间:2016-11-20 08:56:37

标签: fortran vectorization gfortran

我正在构建一些单元测试,并发现我的代码在向量化时会给出略微不同的结果。在下面的示例中,数组a在一个维度中求和并添加到初始值xa的大多数元素都太小而无法更改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

我知道某些编译器选项可以改变答案,例如-fassociative-math。但是,根据gcc optimization options页面,这些都不包含在标准-O3优化包中。

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

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

1 个答案:

答案 0 :(得分:0)

我将您提供的代码复制到一个名为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