Atomic Counters in Co-array Fortran

Our example demonstrates the use of atomic operations for managing shared state across multiple parallel processes. In Co-array Fortran, we’ll use atomic subroutines to achieve similar functionality.

program atomic_counters
  use iso_fortran_env
  implicit none

  integer(atomic_int_kind) :: ops[*]
  integer :: i, c

  ! Initialize the atomic counter
  call atomic_define(ops, 0)

  ! Synchronize all images before starting
  sync all

  ! Each image will increment the counter 1000 times
  do c = 1, 1000
    call atomic_add(ops, 1)
  end do

  ! Wait for all images to complete
  sync all

  ! Image 1 prints the final result
  if (this_image() == 1) then
    print *, "ops:", ops
  end if

end program atomic_counters

In this Co-array Fortran program:

  1. We use an atomic integer ops as a co-array, which is shared across all images (similar to goroutines in the original example).

  2. The atomic_define subroutine is used to initialize the counter to 0.

  3. Instead of creating separate goroutines, we utilize the parallel nature of Co-array Fortran where each image (similar to a process) runs the same code.

  4. The atomic_add subroutine is used to atomically increment the counter, ensuring thread-safety.

  5. We use sync all statements to ensure all images have completed their operations before reading the final value.

  6. Finally, we print the result from image 1 (equivalent to the main thread in the original example).

To run the program:

$ gfortran -coarray=single atomic_counters.f90 -o atomic_counters
$ ./atomic_counters
ops: 1000

Note that this example assumes a single-image execution for simplicity. In a multi-image environment, you would see a larger final value, as each image would contribute 1000 increments.

When running with multiple images (e.g., 50 images to match the original example):

$ gfortran -coarray=lib atomic_counters.f90 -o atomic_counters
$ cafrun -n 50 ./atomic_counters
ops: 50000

This demonstrates the power of atomic operations in Co-array Fortran for managing shared state across parallel processes, similar to the use of atomic operations in concurrent programming with other languages.