from the docs, seems like TaskLocalRNG has some distinct advantages like being reproducible in multithread environment. Thatās why itās chosen as the default.
Thatās not right. There is only one task local RNG per task, so Random.seed!(Random.TaskLocalRNG(), 123) does not create a new RNG. It seeds the existing one. Thatās also why there isnāt a Random.TaskLocalRNG(::Int64) method - you canāt make a new task local RNG.
seed!(x::Int) is equivalent to seed!(Random.TaskLocalRNG(), x::Int), except that the former also stores the used seed, such that it can be āresetā later by functionality such as @testset.
The TaskLocalRNG is a zero-sized struct that refers to four of the five rngState fields in the existing task:
julia> dump(Task)
mutable struct Task <: Any
[... more fields ...]
rngState0::UInt64
rngState1::UInt64
rngState2::UInt64
rngState3::UInt64
rngState4::UInt64
[... more fields ...]
TIL. Apparently Random.TaskLocalRNG() always returns the same object:
julia> let
r1 = Random.TaskLocalRNG()
r2 = Random.TaskLocalRNG()
r1 === r2
end
true
So the object returned by Random.TaskLocalRNG() is always the same. Thatās the first time I see a constructor that doesnāt return a new objectā¦
As a side note, why not make Random.TaskLocalRNG(seed::Int) = Random.seed!(Random.TaskLocalRNG(), seed)? That would make the API equivalent to, say, Xoshiro and MersenneTwister.