在 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
- 使用 'm = 10000000'(7 个零)我得到:
总和 1 = -2014260032
总和 2 = -1146784608
原子时间:1.13900006 s
总和 1 = -2014260032
总和 2 = -2014260032
关键时间:4.09000015 秒
- 使用 '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)
测试 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
- 使用 'm = 10000000'(7 个零)我得到:
总和 1 = -2014260032
总和 2 = -1146784608
原子时间:1.13900006 s
总和 1 = -2014260032
总和 2 = -2014260032
关键时间:4.09000015 秒
- 使用 '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)