-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
define Random.GLOBAL_RNG = TaskLocalRNG()
#51388
Conversation
Random.GLOBAL_RNG == TaskLocalRNG()
Random.GLOBAL_RNG = TaskLocalRNG()
stdlib/Random/src/RNGs.jl
Outdated
# But the global RNG is now TaskLocalRNG() and doesn't store its seed; in order to not break `@testset`, we now | ||
# store the seed used in a call like `seed!(seed)` *without* an explicit RNG in `GLOBAL_SEED`; the wording of the | ||
# feature above was sufficiently unprecise (e.g. what exactly is the "global RNG"?) that this solution seems fine; | ||
# in a breaking julia version, we would probably remove GLOBAL_SEED and this `@testset` feature |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Really? I think this feature is really useful. I actually was just recently looking into ways to add more things to be reset at the start and end of each @testset
(think additional RNGs and other global state). Unfortunately the only way I could see was to create my own AbstractTestSet
subtype and then change every single @testset
invocation to explicitly specify that type, which is rather a burden :-(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh that's the first time i see positive feedback on this feature, it has usually been "meh" ! So this comment about removing this was more of a reflexion of what I thought was the general sentiment; and also the fact that since we don't use MersenneTwister
as the global RNG, there is a mismatch between what we say:
Before the execution of the body of a @testset, there is an implicit call to Random.seed!(seed) where seed is the current seed of the global RNG.
and what we can do, because the "global RNG" doesn't have a seed (there are multiple global RNGs, one per task, and we currently store only one seed.
But anyway, i'm very glad to see someone else finding this feature useful :) I will remove this comment, and i have a plan to make the feature more robust.
`GLOBAL_RNG` is a relic from pre-v1.3 when our global RNG was not thread-safe: ``` const GLOBAL_RNG = MersenneTwister() ``` `GLOBAL_RNG` was never really specified, but was referred to in docstrings (of `rand` etc.), and people had to use it in packages in order to provide a default RNG to their functions. So we have to keep defining `Random.GLOBAL_RNG` for the time being. In v1.3, we didn't have anymore a unique global RNG, but instead one per thread; in order to keep code using `GLOBAL_RNG` working, we got this new definition as a clever hack: ``` struct _GLOBAL_RNG <: AbstractRNG end const GLOBAL_RNG = _GLOBAL_RNG() ``` I.e. `GLOBAL_RNG` is just a handle, which refers to the actual threaded-RNG when passed to `rand` functions. This entails defining most functions taking a `default_rng()` to also accept `_GLOBAL_RNG`. See #41123, and #41235 which is not even merged. But since v1.7, we have `Random.default_rng()` (our now official way to refer to the default RNG) returning `TaskLocalRNG()`, which is itself a "handle" (singleton). So we can as well redefine `GLOBAL_RNG` to be `TaskLocalRNG()` instead of `_GLOBAL_RNG()`, with the advantage that all the relevant methods are already defined for `TaskLocalRNG`. The only expected difference in behavior is `seed!(GLOBAL_RNG, seed)`, which previously would update `Random.GLOBAL_SEED` (a hack used by `@testset`), but now is equivalent to `seed!(TaskLocalRNG(), seed)`, which doesn't update this global seed. This precise behavior was never documented (`GLOBAL_SEED` is purely internal), so this should be fine.
24681c5
to
95325b6
Compare
`GLOBAL_RNG` is a relic from pre-v1.3 when our global RNG was not thread-safe: ``` const GLOBAL_RNG = MersenneTwister() ``` `GLOBAL_RNG` was never really specified, but was referred to in docstrings (of `rand` etc.), and people had to use it in packages in order to provide a default RNG to their functions. So we have to keep defining `Random.GLOBAL_RNG` for the time being. In v1.3, we didn't have anymore a unique global RNG, but instead one per thread; in order to keep code using `GLOBAL_RNG` working, we got this new definition as a clever hack: ``` struct _GLOBAL_RNG <: AbstractRNG end const GLOBAL_RNG = _GLOBAL_RNG() ``` I.e. `GLOBAL_RNG` is just a "handle", which refers to the actual threaded-RNG when passed to `rand` functions. This entails defining most functions taking a `default_rng()` to also accept `_GLOBAL_RNG`. See JuliaLang/julia#41123, and JuliaLang/julia#41235 which is not even merged. But since v1.7, we have `Random.default_rng()` (our now official way to refer to the default RNG) returning `TaskLocalRNG()`, which is itself a "handle" (singleton). So we can as well redefine `GLOBAL_RNG` to be `TaskLocalRNG()` instead of `_GLOBAL_RNG()`, with the advantage that all the relevant methods are already defined for `TaskLocalRNG`. The only expected difference in behavior is `seed!(GLOBAL_RNG, seed)`, which previously would update `Random.GLOBAL_SEED` (a hack used by `@testset`), but now is equivalent to `seed!(TaskLocalRNG(), seed)`, which doesn't update this global seed. This precise behavior was never documented (`GLOBAL_SEED` is purely internal), so this should be fine.
GLOBAL_RNG
is a relic from pre-v1.3 when our global RNG was not thread-safe:GLOBAL_RNG
was never really specified, but was referred to in docstrings (ofrand
etc.), and people had to use it in packages in order to provide a default RNG to their functions. So we have to keep definingRandom.GLOBAL_RNG
for the time being.In v1.3, we didn't have anymore a unique global RNG, but instead one per thread; in order to keep code using
GLOBAL_RNG
working, we got this new definition as a clever hack:I.e.
GLOBAL_RNG
is just a handle, which refers to the actual threaded-RNG when passed torand
functions. This entails defining most function taking adefault_rng()
to also accept_GLOBAL_RNG
. See #41123, and #41235 which is not even merged.But since v1.7, we have
Random.default_rng()
(our now official way to refer to the default RNG) returningTaskLocalRNG()
, which is itself a "handle" (singleton). So we can as well redefineGLOBAL_RNG
to beTaskLocalRNG()
instead of_GLOBAL_RNG()
, with the advantage that all the relevant methods are already defined forTaskLocalRNG
.The only expected difference in behavior is
seed!(GLOBAL_RNG, seed)
, which previously would updateRandom.GLOBAL_SEED
(a hack used by@testset
), but now is equivalent toseed!(TaskLocalRNG(), seed)
, which doesn't update this global seed. This precise behavior was never documented (GLOBAL_SEED
is purely internal), so this should be fine.