Skip to content

Commit b680b4e

Browse files
authored
faster rand(::RandomDevice, NTuple{N, UInt}) (#58288)
`RandomDevice` uses `Libc.getrandom!` to get randomness, but such a system call is very expensive. There was already a specialization for `rand!` on `Array`s of builtin integers to have only one such call; this commit adds a similar specialization for homogeneous tuples of builtin integer types. One motivation is to instantiate `Xoshiro()` faster from `RandomDevice()`, as requesting 4*64 bits of entropy from the system in one go is, at least on my system, roughly 4 times faster that in 4 calls: ``` julia> @Btime Xoshiro() 1.225 μs (1 allocation: 48 bytes) # master 319.068 ns (1 allocation: 48 bytes) # PR ```
1 parent 0682158 commit b680b4e

File tree

3 files changed

+22
-6
lines changed

3 files changed

+22
-6
lines changed

stdlib/Random/src/RNGs.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ seed!(rng::RandomDevice, ::Nothing) = rng
1616

1717
rand(rd::RandomDevice, sp::SamplerBoolBitInteger) = Libc.getrandom!(Ref{sp[]}())[]
1818
rand(rd::RandomDevice, ::SamplerType{Bool}) = rand(rd, UInt8) % Bool
19+
20+
# specialization for homogeneous tuple types of builtin integers, to avoid
21+
# repeated system calls
22+
rand(rd::RandomDevice, sp::SamplerTag{Ref{Tuple{Vararg{T, N}}}, Tuple{S}}
23+
) where {T, N, S <: SamplerUnion(Base.BitInteger_types...)} =
24+
Libc.getrandom!(Ref{gentype(sp)}())[]
25+
1926
function rand!(rd::RandomDevice, A::Array{Bool}, ::SamplerType{Bool})
2027
Libc.getrandom!(A)
2128
# we need to mask the result so that only the LSB in each byte can be non-zero

stdlib/Random/src/Xoshiro.jl

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,7 @@ hash(x::Union{TaskLocalRNG, Xoshiro}, h::UInt) = hash(getstate(x), h + 0x49a62c2
245245

246246
function seed!(rng::Union{TaskLocalRNG, Xoshiro}, ::Nothing)
247247
# as we get good randomness from RandomDevice, we can skip hashing
248-
rd = RandomDevice()
249-
s0 = rand(rd, UInt64)
250-
s1 = rand(rd, UInt64)
251-
s2 = rand(rd, UInt64)
252-
s3 = rand(rd, UInt64)
253-
initstate!(rng, (s0, s1, s2, s3))
248+
initstate!(rng, rand(RandomDevice(), NTuple{4, UInt64}))
254249
end
255250

256251
seed!(rng::Union{TaskLocalRNG, Xoshiro}, seed) =

stdlib/Random/test/runtests.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -824,6 +824,20 @@ end
824824
@inferred rand(Tuple{Int32,Int64,Float64})
825825
@inferred rand(NTuple{20,Int})
826826
@test_throws TypeError rand(Tuple{1:2,3:4})
827+
828+
@testset "rand(::RandomDevice, ::Type{NTuple{N, Int}})" begin
829+
# RandomDevice has a specialization for homogeneous tuple types of builtin integers
830+
rd = RandomDevice()
831+
@test () == rand(rd, Tuple{})
832+
xs = rand(rd, Tuple{Int, Int})
833+
@test xs isa Tuple{Int, Int} && xs[1] != xs[2]
834+
xs = rand(rd, NTuple{2, Int})
835+
@test xs isa Tuple{Int, Int} && xs[1] != xs[2]
836+
xs = rand(rd, Tuple{Int, UInt}) # not NTuple
837+
@test xs isa Tuple{Int, UInt} && xs[1] != xs[2]
838+
xs = rand(rd, Tuple{Bool}) # not included in the specialization
839+
@test xs isa Tuple{Bool}
840+
end
827841
end
828842

829843
@testset "GLOBAL_RNG" begin

0 commit comments

Comments
 (0)