在 OMP (gfortran) 中使用原子的奇怪结果

Strange results using atomic in OMP (gfortran)

测试 Atomic 示例代码我得到了一个奇怪的结果。

program atomic

    use omp_lib
    implicit none

    integer, parameter :: num_threads = 4, m = 1000000
    integer :: thread_num
    integer :: i, j, sum1 = 0, sum2 = 0, tic,toc, rate 
    real:: time
    integer, external :: increment

    thread_num = 0
    !$ call omp_set_num_threads(num_threads)

!////////// ATOMIC ////////////////////////////////////////////////////////////


  CALL system_clock(count_rate=rate)
  call system_clock(tic)
    !$omp parallel do private(thread_num, j) &
    !$omp shared(sum1, sum2)
        do i = 0 , m-1
            !$ thread_num = omp_get_thread_num()
            
            !$omp atomic
                sum1 = sum1 + i
                sum2 = sum2 + increment(thread_num, i)    
        end do
    !$omp end paralleldo

    print*, "sum 1 = ", sum1
    print*, "sum 2 = ", sum2
  call system_clock(toc)
    time = real(toc-tic)/real(rate)
    print*, "Time atomic: ", time, 's'

!////////// CRITICAL ////////////////////////////////////////////////////////////
sum1=0; sum2=0
  CALL system_clock(count_rate=rate)
  call system_clock(tic)
    !$omp parallel do private(thread_num, j) &
    !$omp shared(sum1, sum2)
        do i = 0 , m-1
            !$ thread_num = omp_get_thread_num()
            
            !$omp critical
                sum1 = sum1 + i
                sum2 = sum2 + increment(thread_num, i)    
            !$omp end critical
        end do
    !$omp end paralleldo

    print*, "sum 1 = ", sum1
    print*, "sum 2 = ", sum2
  call system_clock(toc)
    time = real(toc-tic)/real(rate)
    print*, "Time critical: ", time, 's'


end program atomic

integer function increment (thread_num, j)
    implicit none
    integer, intent(in) :: thread_num, j

!    print*, "Function increment run by thread number: ", thread_num
    increment = j

end function increment


  1. 使用 'm = 10000000'(7 个零)我得到:

总和 1 = -2014260032

总和 2 = -1146784608

原子时间:1.13900006 s

总和 1 = -2014260032

总和 2 = -2014260032

关键时间:4.09000015 秒


  1. 使用 'm=1000000'(6 个零)我得到:

总和 1 = 1783293664

总和 2 = 1576859165

原子时间:0.123999998 s

总和 1 = 1783293664

总和 2 = 1783293664

关键时间:0.133000001 秒


我有两个问题:

为什么我在第一种情况下得到负输出?

为什么原子输出中的 sum1 不等于 sum2?


编译使用:

gfortran -Wall -Wextra -fopenmp -O2 -Wall -o prog.exe prueba.f90

./prog.exe

Why do I get a negative output in the first case?

因为求和运算溢出了。从这个source可以读到:

In computer programming, an integer overflow occurs when an arithmetic operation attempts to create a numeric value that is outside of the range that can be represented with a given number of digits – either higher than the maximum or lower than the minimum representable value

对于 m = 10000000,结果是 49999995000000,该值大于 Fortran 中 Integer(32 位整数)可表示的最大值。

第二题

Why is not sum1 equal to sum2 in atomic outputs?

因为 atomic 子句仅应用于操作:

sum1 = sum1 + i

您可以通过使用可以表示更大范围数字的数据类型来解决第一个问题。第二个问题你可以解决如下:

        !$omp atomic
            sum1 = sum1 + i
        !$omp atomic
            sum2 = sum2 + increment(thread_num, i)