# Multithreaded random: different behaviour in Julia \>= 1.5 (not just different stream)

**URL:** <https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291>\
**Category:** General Usage\
**Tags:** question, multithreading, random\
**Created:** [March 16, 2021, 10:26am UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291 "2021-03-16T10:26:37Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [March 16, 2021, 10:26am UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/1 "2021-03-16T10:26:37Z")

</div>

Hi, I just realised that my solution below for multithreaded random number generators works only in Julia \>= 1.5. In particular the test of “CASE A” fails, i.e. the two repetitions issue the same number.  
Is there a way to get the same framework working on Julia \<= 1.4 ?

```julia
using Random, Test, Future, Statistics

FIXEDRNG = MersenneTwister(123)

function generateParallelRngs(rng::AbstractRNG, n::Integer)
    step = rand(rng,big(10)^20:big(10)^40) # making the step random too !
    rngs = Vector{Union{MersenneTwister,Random._GLOBAL_RNG}}(undef, n) # Vector{typeof(rng)}(undef, n)
    rngs[1] = copy(rng)
    for i = 2:n
        rngs[i] = Future.randjump(rngs[i-1], step)
    end
    return rngs
end

# ==================================
# New test
println("** Testing generateParallelRngs()...")
x = rand(100)

function innerFunction(bootstrappedx; rng=Random.GLOBAL_RNG)
     sum(bootstrappedx .* rand(rng) ./ 0.5)
end
function outerFunction(x;rng = Random.GLOBAL_RNG)
    rngs = generateParallelRngs(rng,Threads.nthreads()) # make new copy instances
    results = Array{Float64,1}(undef,30)
    Threads.@threads for i in 1:30
        tsrng = rngs[Threads.threadid()] # Thread safe random number generator
        toSample = rand(tsrng, 1:100,100)
        bootstrappedx = x[toSample]
        innerResult = innerFunction(bootstrappedx, rng=tsrng)
        results[i] = innerResult
    end
    overallResult = mean(results)
    return overallResult
end

# Case A - Different sequences..
@test outerFunction(x) != outerFunction(x)

# Case B - Different values, but same sequence..
mainRng = copy(FIXEDRNG)
a = outerFunction(x, rng=mainRng)
b = outerFunction(x, rng=mainRng)

mainRng = copy(FIXEDRNG)
A = outerFunction(x, rng=mainRng)
B = outerFunction(x, rng=mainRng)

@test a != b && a == A && b == B

# Case C - Same value at each call
a = outerFunction(x,rng=copy(FIXEDRNG))
b = outerFunction(x,rng=copy(FIXEDRNG))
@test a == b

```

---

<div class="post-metadata">

**Author:** ![kristoffer.carlsson](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kristoffer.carlsson/32/22_2.png) [@kristoffer.carlsson](https://discourse.julialang.org/u/kristoffer.carlsson)\
**Post date:** [March 16, 2021, 10:29am UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/2 "2021-03-16T10:29:48Z")

</div>

> [@sylvaticus](#):
>
> Hi, I just realised that my solution below for multithreaded random number generators works only in Julia

Just as a note, the default RNG is now thread safe so there is no need to roll your own. You can just use `rand()` from different threads.

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [March 16, 2021, 10:34am UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/3 "2021-03-16T10:34:57Z")

</div>

Thank you, but how can I obtain then the three behaviours I need: (1) completely random; (2) script fixed random (each time I run the script/sequence I obtain the same sequence of results) and (3) completely fixed: each time I call the function I obtain the same result.

I pass the functions a seed instead of the RNG ?

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [March 19, 2021, 10:31pm UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/4 "2021-03-19T22:31:39Z")

</div>

A faster, more readable, working with many RNG and Julia versions:

```julia
function generateParallelRngs1(rng::AbstractRNG, n::Integer)
    seeds = [rand(rng,1:18446744073709551615) for i in 1:n]
    rngs = [copy(rng) for i in 1:n]
    return Random.seed!.(rngs,seeds)
end

```

---

<div class="post-metadata">

**Author:** ![CodeLenz](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/codelenz/32/3419_2.png) [@CodeLenz](https://discourse.julialang.org/u/CodeLenz)\
**Post date:** [March 20, 2021, 12:21pm UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/5 "2021-03-20T12:21:55Z")

</div>

> [@sylvaticus](#):
>
> and (3) completely fixed: each time I call the function I obtain the same result.

[https://github.com/JuliaRandom/StableRNGs.jl](https://github.com/JuliaRandom/StableRNGs.jl)

---

<div class="post-metadata">

**Author:** ![sylvaticus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/sylvaticus/32/203883_2.png) [@sylvaticus](https://discourse.julialang.org/u/sylvaticus)\
**Post date:** [March 20, 2021, 12:49pm UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/6 "2021-03-20T12:49:07Z")

</div>

Yes, my second version works with StableRNGs.

To be clear, you obtain the “fixed” results also with `MersenneTwister(123)` (or with `Random.seed!(123)`). The only difference (performances apart) is that StableRNGs streams are guaranteed to remain invariant, while default RNG outputs could change even between minor Julia versions, so you make a certain test with a Julia vetsion and it’s fine, but then it may breack on newer versions just because the random stream has changed. This make StableRNGs perfect for your package tests where stochasticiity is involved.

---

<div class="post-metadata">

**Author:** ![tkf](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tkf/32/17635_2.png) [@tkf](https://discourse.julialang.org/u/tkf)\
**Post date:** [March 23, 2021, 7:20am UTC](https://discourse.julialang.org/t/multithreaded-random-different-behaviour-in-julia-1-5-not-just-different-stream/57291/7 "2021-03-23T07:20:45Z")

</div>

FYI, [How to do X in parallel? · FLoops](https://juliafolds.github.io/FLoops.jl/stable/howto/parallel/#Efficient-and-reproducible-usage-patterns-of-random-number-generators) discusses a couple of approaches for parallel RNG use cases, including the ones that can reproduce RNG outputs.
