Hi, thank you kindly for your response. I have tried some more things in light of your comments, and I’m starting to think that I’m dealing with multiple problems. Let me first provide a minimally reduced version of the code. In C++ I have the wrappers
extern "C" {
void* init_rng(int n_threads, int seed);
double runif_par(void* rng_ptr, int i_thread);
}
void* init_rng(int n_threads, int seed) {
using rng_state_type = dust::random::generator<double>;
void* rng_ptr = (void*) new dust::random::prng<rng_state_type>(n_threads, seed);
return(rng_ptr);
}
double runif_par(void* rng_ptr, int i_thread) {
using rng_state_type = dust::random::generator<double>;
using prng = dust::random::prng<rng_state_type>;
prng* rng_ptr_ = static_cast<prng*>(rng_ptr);
auto& state = rng_ptr_->state(i_thread);
double sample = dust::random::random_real<double>(state);
return(sample);
}
I put the Fortran interface in a module which I include in the main business code:
module interface
use, intrinsic :: iso_c_binding
implicit none
interface
function init_rng(n_threads, seed) result(res) bind(c)
import
integer(c_int), value :: seed
integer(c_int), value :: n_threads
type(c_ptr) :: res
end function init_rng
function runif_par(rng, i_thread) result(sample) bind(c)
import
type(c_ptr), value :: rng
integer(c_int), value :: i_thread
real(c_double) :: sample
end function runif_par
Now the main loop does something like this:
n_threads = init_omp() ! Some setup to determine number of threads to use.
rng = init_rng(n_threads, 42) ! Second argument is seed.
!$omp parallel do num_threads(n_threads) private(array) schedule(dynamic, 25)
do i_sim = 1, n_config
i_thread = omp_get_thread_num()
allocate(array(n))
array = 0
call internal_subroutine(array)
deallocate(array)
end do
Here internal_subroutine
does some the heavy lifting involving calculations which call the RNG functions. I’ve also written a test routine which simply generates uniform samples on each thread and checks whether there are any duplicates (i.e. whether the random number generation works well).
Now, what I’ve found since posting the original question is that the RNG works if I add an additional step which manually updates the internal state. To be precise, the state
variable in runif_par
is updated inside the C++ library code by calling next
each time a draw is made. If I manually add a call to next
inside the wrapper, then it works without problem. So it seems that there is some lag which causes the next call from the Fortran to access the state before it’s updated (which is very odd).