Section Computing Pi: Compute Pi with OpenMP#
Adapted from: gjbex/Fortran-MOOC
This program demonstrates computing \(\pi\) in Fortran using OpenMP. The program uses the following equation for computation:#
\[
\Large \pi = 4 \int_{0}^{1} \sqrt{1-x^2}dx
\]
In file section_computing_pi_compute_pi_openmp.f90#
program compute_pi_omp
use, intrinsic :: iso_fortran_env, only : DP => REAL64, I8 => INT64
implicit none
integer(kind=I8) :: i, nr_iters
real(kind=DP) :: delta, x, pi_val
real(kind=DP) :: pi_val_actual
pi_val = 0.0_DP
pi_val_actual = 2.0_DP * acos(0.0_DP)
nr_iters = get_nr_iters()
delta = 1.0_DP/nr_iters
x = 0.0_DP
!$omp parallel do default(none) private(x) shared(delta, nr_iters) reduction(+:pi_val)
do i = 1, nr_iters
x = i*delta
pi_val = pi_val + sqrt(1.0_DP - x**2)
end do
!$omp end parallel do
pi_val = 4.0_DP*pi_val/nr_iters
print '(A, I15, A, F25.15)', "After ", nr_iters, " loops, Pi = ", pi_val
print '(A, F25.15)', "Actual value of Pi = ", pi_val_actual
print '(A, F25.15)', "Absolute difference = ", abs(pi_val - pi_val_actual)
contains
function get_nr_iters() result(nr_iters)
use, intrinsic :: iso_fortran_env, only : error_unit
implicit none
integer(kind=I8) :: nr_iters
integer(kind=I8), parameter :: default_nr_iters = 1000_I8
character(len=1024) :: buffer, msg
integer :: istat
if (command_argument_count() >= 1) then
call get_command_argument(1, buffer)
read (buffer, fmt=*, iostat=istat, iomsg=msg) nr_iters
if (istat /= 0) then
write (unit=error_unit, fmt='(2A)') &
'error: ', msg
stop 1
end if
else
nr_iters = default_nr_iters
end if
end function get_nr_iters
end program compute_pi_omp
The above program is compiled and run using Fortran Package Manager (fpm):
Build the Program using FPM (Fortran Package Manager)#
import os
root_dir = ""
root_dir = os.getcwd()
Since this program uses the OpenMP external library, the fpm.toml file had to be augmented as below.
name = "Section_Computing_Pi_Compute_Pi_OpenMP"
[build]
auto-executables = true
auto-tests = true
auto-examples = true
[dependencies]
openmp="*"
[install]
library = false
[[executable]]
name="Section_Computing_Pi_Compute_Pi_OpenMP"
source-dir="app"
main="section_computing_pi_compute_pi_openmp.f90"
code_dir = root_dir + "/" + "Fortran_Code/Section_Computing_Pi_Compute_Pi_OpenMP"
os.chdir(code_dir)
We use FPM (Fortran Package Manager) to build and link OpenMP into the executable with the –flag -fopenmp switches.
build_status = os.system("fpm build --flag -fopenmp 2>/dev/null")
Run the Program using FPM (Fortran Package Manager)#
10 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 10 2>/dev/null > output.txt")
76.3 ms ± 16.6 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 10 loops, Pi = 2.904518326248318
Actual value of Pi = 3.141592653589793
Absolute difference = 0.237074327341475
100 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 100 2>/dev/null > output.txt")
67.7 ms ± 16.3 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 100 loops, Pi = 3.120417031779045
Actual value of Pi = 3.141592653589793
Absolute difference = 0.021175621810748
1,000 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 1000 2>/dev/null > output.txt")
51.3 ms ± 15.7 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 1000 loops, Pi = 3.139555466911028
Actual value of Pi = 3.141592653589793
Absolute difference = 0.002037186678765
10,000 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 10000 2>/dev/null > output.txt")
56.8 ms ± 12 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 10000 loops, Pi = 3.141391477611324
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000201175978469
100,000 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 100000 2>/dev/null > output.txt")
54.4 ms ± 29.1 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 100000 loops, Pi = 3.141572616401988
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000020037187805
1,000,000 Iterations#
%%timeit -r 10 -n 1
exec_status = os.system("fpm run -- 1000000 2>/dev/null > output.txt")
47 ms ± 9.33 ms per loop (mean ± std. dev. of 10 runs, 1 loop each)
%cat output.txt
After 1000000 loops, Pi = 3.141590652413799
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000002001175994
10,000,000 Iterations#
%%timeit -r 10 -n 2
exec_status = os.system("fpm run -- 10000000 2>/dev/null > output.txt")
51.6 ms ± 10.1 ms per loop (mean ± std. dev. of 10 runs, 2 loops each)
%cat output.txt
After 10000000 loops, Pi = 3.141592453552619
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000000200037174
100,000,000 Iterations#
%%timeit -r 10 -n 2
exec_status = os.system("fpm run -- 100000000 2>/dev/null > output.txt")
108 ms ± 18 ms per loop (mean ± std. dev. of 10 runs, 2 loops each)
%cat output.txt
After 100000000 loops, Pi = 3.141592633588637
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000000020001156
1,000,000,000 Iterations#
%%timeit -r 10 -n 2
exec_status = os.system("fpm run -- 1000000000 2>/dev/null > output.txt")
588 ms ± 51.1 ms per loop (mean ± std. dev. of 10 runs, 2 loops each)
%cat output.txt
After 1000000000 loops, Pi = 3.141592651589789
Actual value of Pi = 3.141592653589793
Absolute difference = 0.000000002000004