Search code examples
multithreadingparallel-processingopenmpatomicgfortran

Strange results using atomic in OMP (gfortran)


Testing an Atomic example code I got a strange result.

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. Using 'm = 10000000' (7 zeros) I get:

sum 1 = -2014260032

sum 2 = -1146784608

Time atomic: 1.13900006 s

sum 1 = -2014260032

sum 2 = -2014260032

Time critical: 4.09000015 s


  1. Using 'm=1000000' (6 zeros) I get:

sum 1 = 1783293664

sum 2 = 1576859165

Time atomic: 0.123999998 s

sum 1 = 1783293664

sum 2 = 1783293664

Time critical: 0.133000001 s


I have two questions:

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

Why is not sum1 equal to sum2 in atomic outputs?


It was compiled using:

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

./prog.exe


Solution

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

    Because the sum operation overflows. From this source one can read:

    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

    For a m = 10000000 the result is 49999995000000, which is a value bigger than the maximum value representable with an Integer (32-bit integer) in Fortran.

    The second question

    Why is not sum1 equal to sum2 in atomic outputs?

    Because the atomic clause is only being applied to the operation:

    sum1 = sum1 + i
    

    The first problem you can solve by using a data-type that can represent a wider range of numbers. The second problem you can solve as follows:

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