Fast random number generator for RGBs

I would like to generate very fast pseudo random images. The quality of the random numbers to generate random RGBs are not very important to be completely independent. But they sould look “random” for a human eye.

Is there a faster way to generate random RGBs as “rand(RGB{Float32},1080,1980)” ?

Well, this is already > 6x faster:

function f2()
   M = Matrix{RGB{Float32}}(undef, 1080, 1980)
   @inbounds for j in 1:1980
       for i in 1:1080
           M[i,j] = RGB(rand(), rand(), rand())
       end
   end
   return M
end
julia> @btime f1(); # f1() = rand(RGB{Float32},1080,1980)
  137.335 ms (3 allocations: 24.47 MiB)

julia> @btime f2();
  20.709 ms (2 allocations: 24.47 MiB)

But of course, you should probably think about algorithmic improvements, i.e. how to utilise the fact that statistical randomness is not the primary objective. On the purely technical side, you might try other RNGs, for example from https://github.com/sunoru/RandomNumbers.jl.

Update:

using RandomNumbers
rng_xor = RandomNumbers.Xorshifts.Xoroshiro128Star()

function f2_rng(rng)
   M = Matrix{RGB{Float32}}(undef, 1080, 1980)
   @inbounds for j in 1:1980
       for i in 1:1080
           M[i,j] = RGB(rand(rng), rand(rng), rand(rng))
       end
   end
   return M
end

I find

julia> @btime f2_rng($rng_xor);
  8.903 ms (2 allocations: 24.47 MiB)
5 Likes

This is a bit simpler than f2, while retaining the same performance:

julia> @btime f2();
  27.606 ms (2 allocations: 24.47 MiB)

julia> function f3()
          M = Matrix{RGB{Float32}}(undef, 1080, 1980)
          @. M = RGB(rand(), rand(), rand())
          return M
       end
f3 (generic function with 1 method)

julia> @btime f3();
  28.184 ms (2 allocations: 24.47 MiB)

ButiIf you want performance you need to explicitly pass the RNG:

julia> rng = Random.default_rng()
MersenneTwister(0x1367e9fe8e33ee99f87edfd09c2e5904, (0, 7916357112, 7916356110, 690))

julia> function f4(rng)
          M = Matrix{RGB{Float32}}(undef, 1080, 1980)
          @. M = RGB(rand(rng), rand(rng), rand(rng))
          return M
       end
f4 (generic function with 1 method)

julia> @btime f4($rng);
  11.616 ms (2 allocations: 24.47 MiB)

julia> using RandomNumbers

julia> rng_xor = RandomNumbers.Xorshifts.Xoroshiro128Star()
RandomNumbers.Xorshifts.Xoroshiro128Star(0x5f9b1be270242ed9, 0x4ddeffa1af6dbc23)

julia> @btime f4($rng_xor);
  11.991 ms (2 allocations: 24.47 MiB)
4 Likes

Oh, interesting. I didn’t know that passing the rng explicitly can have such a big impact.

Yes, the speedup you’re seeing with Xor is only due to the fact you passed the RNG explicitly. Accessing the default global RNG takes time.

8 Likes

A good explanation of why one needs to pass rng explicitly in In Julia 1.5 is here: https://bkamins.github.io/julialang/2020/11/20/rand.html

6 Likes

Thank you all! I learned a lot here!

Hi,

I would like to follow. Does anyone knows about an ultra-fast approach to generate random values in mod-3 algebra, i.e. 0,1,2? So far, I use

rand(rng, 0:2),

where rng = Xorshift64Star. I wonder if there might be something even faster?

Thanks a lot.

that should be full speed. (although technically it might be slightly faster to generate a small SVector of them since it should then be able to use fewer bits of entropy)

Thanks a lot.

Too bad there is no silver bullet.

There is a specialization for small tuples, such that rand(rng, (1, 2)) is faster than rand(rng, 1:2). For three elements the benefit seem negligible though.