For historical reasons, initializing a variable has an implicit save behavior:
real(8) :: d = 0.0d0
is equal to
real(8), save :: d = 0.0d0
This is different from a separate declaration and assignment, i.e.
real(8) :: d
d = 0.0d0
The purpose of the save
attribute is to save the value between calls of the function, implying the variables is placed in static storage and are persistent for the duration of the program. Another way I personally think of it is like a global variable, but limited to the scope of the function, if that makes sense. Or you could imagine it like a “function with memory”; the value of d
is remembered across procedure calls.
Now in a multi-threaded program, each thread calling brent_pow
will be referring to the same save
’d variables thereby “stepping on each others toes”, i.e. modifying the values that another thread is still using. This is known as a data race. You could solve this issue by putting the function call in a single
construct:
!$omp parallel do collapse(2) private(inda, indb, aval, bval, xout, yout)
do inda = 1, anum
do indb = 1, bnum
aval = agrid(inda)
bval = bgrid(indb)
!$omp single
call fmintest(aval, bval, xout, yout)
!$omp end single
savemat(indb, inda, :) = [ xout, yout ]
end do
end do
but this would force the execution to become sequential (the threads would enter the function exclusively, i.e. one-by-one). Instead, by removing the saved variables, each thread keeps their own copy of d
and the data-race is resolved.
The implicit save
is a very common “gotcha” in Fortran. You can find other explanations at the following links:
- Fortran assignment on declaration and SAVE attribute gotcha
- Why is there an implied SAVE attribute in Fortran?
- Fortran GOTCHAS: Implied save
I’ve also shown some examples related to save
here: