In Julia 1.3 each thread has its own default global RNG. Calling rand() in each thread is safe. So the following code that calculates pi returns correct result:
function par_pi(n::Int)
hits = zeros(Int, nthreads())
@threads for i in 1:n
x, y = rand(), rand()
hits[threadid()] += (x^2 + y^2 <= 1)
end
4.0 * sum(hits) / n
end
@time par_pi(10_000_000)
But the above code runs 4x slower than the following version that uses manually allocated RNGs:
const threadsRNG = [MersenneTwister() for i in 1:nthreads()]
function par_pi2(n::Int)
hits = zeros(Int, nthreads())
@threads for i in 1:n
rng = threadsRNG[threadid()]
x, y = rand(rng), rand(rng)
hits[threadid()] += (x^2 + y^2 <= 1)
end
4.0 * sum(hits) / n
end
@time par_pi2(10_000_000)
I see the similar results with the latest nightly. It’s strange because @code_lowered is basically the same for both functions. Maybe worth filing a bug report.
julia> @btime par_pi(10^7)
81.293 ms (44 allocations: 5.89 KiB)
3.140466
julia> @btime par_pi2(10^7)
27.520 ms (44 allocations: 5.89 KiB)
3.14193
julia> versioninfo()
Julia Version 1.4.0-DEV.670
Commit 032dbe33e3 (2019-12-29 22:39 UTC)
Platform Info:
OS: Linux (x86_64-pc-linux-gnu)
CPU: Intel(R) Core(TM) i7-7700 CPU @ 3.60GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-8.0.1 (ORCJIT, skylake)
Environment:
JULIA_NUM_THREADS = 8
EDIT:
The difference in the lowered code is the following line. I’m not sure how to read it. Do the numbers indicate a different version of the function? Main.:(var"#19#threadsfor_fun#4") Main.:(var"#2#threadsfor_fun#3")
@robsmith11, thank you for confirming the result. Indeed it is strange; Two lowered code are same except for the generated variable names. I will file a bug report.