This page compares the computational speeds of Fortran, Python, and Julia. It is often said that Python is slow, or that Julia is as fast as Fortran, but when comparing the computational speeds between languages, the comparisons are sometimes made using highly optimized libraries or by only optimizing the code written in a specific language, which may not always lead to meaningful comparisons for many users. Therefore, this time, we will compare the computational speeds of Fortran, Python, and Julia by calculating pi using the relatively simple problem of the Monte Carlo method. To fairly compare the computational speeds of different languages, we will use simple code that does not undergo particular optimization (assuming the kind of code that an average student who has just started learning numerical calculation in a university course would write).
To compare the computational speeds of Fortran, Python, and Julia, we will use a relatively simple Monte Carlo calculation that involves randomly placing points within the (0,1)×(0,1) area and estimating pi by counting the number of points within a distance of 1 from the origin (0,0). If the total number of points is \(N_{total}\) and the number of points inside the circle is \(N_{inside}\), then pi (π) can be approximated as follows: \[ \pi \approx \frac{4N_{inside}}{N_{total}}. \tag {1} \] By placing a very large number of points randomly, we can approximate pi with greater accuracy.
In this comparison of computational speeds, we use a common random number generation routine to fairly compare the speeds of different languages. Specifically, we prepare a random number generation routine using the linear congruential method for the Monte Carlo calculation.
To compare the computational speeds of Fortran, Python, and Julia, we will calculate an approximate value of pi using the Monte Carlo method. In this calculation, we implement the linear congruential method as a common random number generation routine in each code, generating 109 random numbers for the calculation. Please check each code for the details of the calculation.
Fortran code
(mc.f90)
program main
implicit none
integer :: seed, i, n
integer :: num_inside, num_points
real(8) :: x, y, r2
seed = 20231226
num_points = 1000000000
num_inside = 0
do i = 1, num_points
call LCGs(seed, x)
call LCGs(seed, y)
r2 = x**2 + y**2
if(r2<1d0)num_inside = num_inside + 1
end do
write(*,*)"pi=",4*dble(num_inside)/num_points
contains
! Linear congruential generators (LCGs)
! Parameters are provided by Park and Miller
! See https://c-faq.com/lib/rand.html
subroutine LCGs(seed, rand_num)
implicit none
integer,parameter :: a = 48271
integer,parameter :: m = 2147483647
integer,parameter :: q = m/a
integer,parameter :: r = mod(m,a)
integer,intent(inout) :: seed
real(8),intent(out) :: rand_num
integer :: hi, lo, test
hi = seed/q
lo = mod(seed, q)
test = a * lo - r * hi
if(test<0)then
seed = test
else
seed = test + m
end if
rand_num = dble(seed)/m
end subroutine LCGs
end program main
Python code
(mc.py)
import numpy as np
# Linear congruential generators (LCGs)
# Parameters are provided by Park and Miller
# See https://c-faq.com/lib/rand.html
def LCGs(seed):
a = 48271
m = 2147483647
q = m // a
r = m % a
hi = seed // q
lo = seed % q
test = a * lo - r * hi
if test > 0:
seed = test
else:
seed = test + m
rand_num = float(seed) / m
return seed, rand_num
seed = 20231226
num_points = 1000000000
num_inside = 0
for i in range(num_points):
seed, x = LCGs(seed)
seed, y = LCGs(seed)
r2 = x**2 + y**2
if r2 < 1.0:
num_inside += 1
print("pi =", 4.0 * float(num_inside) / num_points)
Julia code
(mc.jl)
# Linear congruential generators (LCGs)
# Parameters are provided by Park and Miller
# See https://c-faq.com/lib/rand.html
function LCGs(seed::Integer)
a = 48271
m = 2147483647
q = m ÷ a
r = m % a
hi = seed テキ q
lo = seed % q
test = a * lo - r * hi
if test > 0
seed = test
else
seed = test + m
end
return seed, Float64(seed) / m
end
function main()
seed = 20231226
num_points = 1000000000
num_inside = 0
for i in 1:num_points
seed, x = LCGs(seed)
seed, y = LCGs(seed)
r2 = x^2 + y^2
if r2 < 1.0
num_inside += 1
end
end
println("pi=", 4.0 * num_inside / num_points)
end
main()
In this comparison, we use gfortran (GNU Fortran) and ifx (Intel Fortran; formerly ifort) as Fortran compilers. GNU Fortran allows specifying optimization options to improve computational speed at compile time. This time, we will measure the computational speeds for four optimization options: O0
, O1
, O2
, and O3
, to also investigate the impact of compilation options on computational speed. For Intel Fortran, in addition to these options, we will add and test the -xHOST
option, which optimizes according to the executing processor.
The Python code (mc.py
) is executed in the terminal with python mc.py
, and the Julia code (mc.jl
) is similarly executed with julia mc.jl
in the terminal. Some may wonder about Julia's compilation time when comparing computational speeds with Fortran, but we have confirmed that this does not affect the results of this comparison, so we will mention this point later.
Executing the prepared Fortran code mc.f90
, Python code mc.py
, and Julia code mc.jl
will output the approximate value of pi by generating 109 random numbers and performing Monte Carlo sampling, and the obtained value will be displayed on the screen. By comparing the execution results of the three codes, we can confirm that each code is functioning correctly.
To compare the computational speeds of Fortran, Python, and Julia, we executed each of the three computation codes five times and measured the computation time for each run. The table below shows the average execution time and standard error obtained from this measurement.
Execution Time Required for Estimating Pi Using the Monte Carlo Method (109 Monte Carlo Trials) | |
---|---|
Language (Compiler Name, Compile Option) | Execution Time and Standard Error |
Fortran (gfortran -O0) | 13.11 ± 0.02 seconds |
Fortran (gfortran -O1) | 8.33 ± 0.00 seconds |
Fortran (gfortran -O2) | 7.91 ± 0.00 seconds |
Fortran (gfortran -O3) | 7.91 ± 0.00 seconds |
Fortran (ifx -O0) [formerly ifort] | 17.51 ± 0.01 seconds |
Fortran (ifx -O1) [formerly ifort] | 8.81 ± 0.03 seconds |
Fortran (ifx -O2) [formerly ifort] | 8.39 ± 0.00 seconds |
Fortran (ifx -O3) [formerly ifort] | 8.39 ± 0.00 seconds |
Fortran (ifx -O3 -xHOST) [formerly ifort] | 8.55 ± 0.00 seconds |
Python | 908.47 ± 3.72 seconds |
Julia | 11.37 ± 0.00 seconds |
From the table above, let's first compare the results for Fortran compilers and compile options. For Fortran compilers, we used the GNU Fortran compiler (gfortran) and Intel Fortran compiler (ifx; formerly ifort). In the case of GNU Fortran, specifying the -O3
option resulted in the fastest outcome, with an execution time of 7.91 seconds. In contrast, for Intel Fortran, specifying the -O3
option also resulted in the fastest outcome, with an execution time of 8.39 seconds. Interestingly, in this case of Monte Carlo calculations using the linear congruential method, the GNU Fortran compiler was about 6% faster than the Intel Fortran compiler. This suggests that the linear congruential method, which mainly involves integer operations, may benefit from GNU Fortran's more advanced optimization for integer calculations compared to Intel Fortran.
Next, comparing the execution time of Fortran (gfortran -O3
) with Python, Fortran is about 115 times faster than Python. Moreover, comparing Fortran (gfortran -O3
) with Julia, Fortran is about 1.4 times faster than Julia. Furthermore, comparing the execution time of Julia with Python, Julia is about 79.9 times faster than Python.
In this comparison, we obtained an interesting result that GNU Fortran can be faster than Intel Fortran in some cases. This difference can be attributed to each compiler's focus on different types of operations. For example, Intel Fortran may emphasize floating-point operations commonly used in scientific and technical computations, which might result in its integer operation performance not keeping up with that of GNU Fortran. Comparing under various conditions might reveal more about each compiler's characteristics.
Moreover, in the case of Monte Carlo calculations like this one, we found that Fortran can compute about 1.4 times faster than Julia. Evaluating what this 1.4 times difference means can be challenging, but imagining how you would feel if the code you use in your own research or work took 1.4 times longer to execute might give some insight into the advantages and disadvantages of Julia.
In the comparison above, the Julia code execution time was measured by running julia mc.jl
, so the compilation time is included in the execution time. Therefore, some people might feel that the comparison between Julia and Fortran is not accurate. As an additional analysis, we measured the execution time with the number of Monte Carlo samples doubled to practically increase the calculation time by twofold and relatively reduce the impact of Julia's compilation time. For Fortran, comparisons are made using the gfortran -O3
compile command.
The results of the additional analysis are shown in the table below. Even when comparing Fortran and Julia's computation speeds using the results of calculations with the practical computation amount doubled, it was verified that Fortran is about 1.4 times faster than Julia.
Execution Time Required for Estimating Pi Using the Monte Carlo Method (2×109 Monte Carlo Trials) | |
---|---|
Language (Compiler Name, Compile Option) | Execution Time and Standard Error |
Fortran (gfortran -O3) | 15.85 ± 0.01 seconds |
Julia | 22.65 ± 0.01 seconds |
This verification was performed in the following environment on December 28, 2023.
[Back to Research and Verification Home]