# Fast random number generator for RGBs

**URL:** https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035
**Category:** Performance
**Created:** [December 18, 2020, 10:43am UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035 "2020-12-18T10:43:37Z")
**Posts on this page:** 12
**Page:** 1

<div class="post-metadata">

### Author: ![boroboro77](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/boroboro77/32/20045_2.png) [@boroboro77](https://discourse.julialang.org/u/boroboro77)
#### Post date: [December 18, 2020, 10:43am UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/1 "2020-12-18T10:43:37Z")

</div>

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)” ?

---

<div class="post-metadata">

### Author: ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)
#### Post date: [December 18, 2020, 11:52am UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/2 "2020-12-18T11:52:44Z")

</div>

Well, this is already \> 6x faster:

```julia
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
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 [GitHub - JuliaRandom/RandomNumbers.jl: Random Number Generators for the Julia Language.](https://github.com/sunoru/RandomNumbers.jl).

**Update:**

```julia
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
julia> @btime f2_rng($rng_xor);
  8.903 ms (2 allocations: 24.47 MiB)
```

---

<div class="post-metadata">

### Author: ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)
#### Post date: [December 18, 2020, 2:10pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/3 "2020-12-18T14:10:37Z")

</div>

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

```julia
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
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)

```

---

<div class="post-metadata">

### Author: ![carstenbauer](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/carstenbauer/32/4981_2.png) [@carstenbauer](https://discourse.julialang.org/u/carstenbauer)
#### Post date: [December 18, 2020, 2:13pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/4 "2020-12-18T14:13:13Z")

</div>

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

---

<div class="post-metadata">

### Author: ![giordano](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/giordano/32/2166_2.png) [@giordano](https://discourse.julialang.org/u/giordano)
#### Post date: [December 18, 2020, 2:14pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/5 "2020-12-18T14:14:12Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![mgr](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mgr/32/8624_2.png) [@mgr](https://discourse.julialang.org/u/mgr)
#### Post date: [December 18, 2020, 2:42pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/6 "2020-12-18T14:42:49Z")

</div>

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](https://bkamins.github.io/julialang/2020/11/20/rand.html)

---

<div class="post-metadata">

### Author: ![boroboro77](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/boroboro77/32/20045_2.png) [@boroboro77](https://discourse.julialang.org/u/boroboro77)
#### Post date: [December 18, 2020, 3:13pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/7 "2020-12-18T15:13:53Z")

</div>

Thank you all! I learned a lot here!

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [January 8, 2021, 7:59pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/8 "2021-01-08T19:59:40Z")

</div>

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

```julia
rand(rng, 0:2),

```

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

Thanks a lot.

---

<div class="post-metadata">

### Author: ![Oscar\_Smith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oscar_smith/32/25343_2.png) [@Oscar\_Smith](https://discourse.julialang.org/u/Oscar_Smith)
#### Post date: [January 8, 2021, 8:30pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/9 "2021-01-08T20:30:25Z")

</div>

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)

---

<div class="post-metadata">

### Author: ![Tomas\_Pevny](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tomas_pevny/32/25466_2.png) [@Tomas\_Pevny](https://discourse.julialang.org/u/Tomas_Pevny)
#### Post date: [January 9, 2021, 6:45am UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/10 "2021-01-09T06:45:18Z")

</div>

Thanks a lot.

Too bad there is no silver bullet.

---

<div class="post-metadata">

### Author: ![rfourquet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rfourquet/32/3610_2.png) [@rfourquet](https://discourse.julialang.org/u/rfourquet)
#### Post date: [January 9, 2021, 8:32am UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/11 "2021-01-09T08:32:30Z")

</div>

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.

---

<div class="post-metadata">

### Author: ![Lilith](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lilith/32/27492_2.png) [@Lilith](https://discourse.julialang.org/u/Lilith)
#### Post date: [October 9, 2021, 5:43pm UTC](https://discourse.julialang.org/t/fast-random-number-generator-for-rgbs/52035/12 "2021-10-09T17:43:10Z")

</div>

`rand(RGB{Float32},1080,1980)` is now faster than everything else recommended in this thread.

It’s been fixed in the (not yet released) commit [https://github.com/JuliaGraphics/ColorTypes.jl/commit/5e3aeb572a84289e1b87524097ea8f94c659f643](https://github.com/JuliaGraphics/ColorTypes.jl/commit/5e3aeb572a84289e1b87524097ea8f94c659f643)) of ColorTypes.jl.

Consolidated benchmarks of every method from this thread after `]develop ColorTypes` and Julia restart (or presumably `]add ColorTypes` once 0.12 is released):

```julia
using RandomNumbers, BenchmarkTools, Random, Colors
rng = Random.default_rng()
rng_xor = RandomNumbers.Xorshifts.Xoroshiro128Star()

f1() = rand(RGB{Float32},1080,1980)

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

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

function f3()
    M = Matrix{RGB{Float32}}(undef, 1080, 1980)
    @. M = RGB(rand(), rand(), rand())
    return M
end

function f4(rng)
    M = Matrix{RGB{Float32}}(undef, 1080, 1980)
    @. M = RGB(rand(rng), rand(rng), rand(rng))
    return M
end

@btime rand(RGB{Float32},1080,1980) seconds=1
# 9.539 ms (4 allocations: 24.47 MiB)

@btime rand($rng, RGB{Float32},1080,1980) seconds=1
# 9.704 ms (4 allocations: 24.47 MiB)

@btime rand($rng_xor, RGB{Float32},1080,1980) seconds=1
# 12.198 ms (4 allocations: 24.47 MiB)

@btime f1() seconds=1
# 9.622 ms (4 allocations: 24.47 MiB)

@btime f2() seconds=1
# 26.670 ms (2 allocations: 24.47 MiB)

@btime f2_rng($rng) seconds=1
# 11.479 ms (2 allocations: 24.47 MiB)

@btime f2_rng($rng_xor) seconds=1
# 11.054 ms (2 allocations: 24.47 MiB)

@btime f3() seconds=1
# 27.571 ms (2 allocations: 24.47 MiB)

@btime f2_rng($rng) seconds=1
# 11.446 ms (2 allocations: 24.47 MiB)

@btime f2_rng($rng_xor) seconds=1
# 11.022 ms (2 allocations: 24.47 MiB)

```

How we got here:

1. Calls to `rand()` are thread safe by assigning each thread its own `MersenneTwister` which comes at a slight performance penalty at the time the thread’s generator is selected.
2. Vectorized `rand(1080, 1980)` calls look up the thread’s generator first with `default_rng()`, and pass that generator along so that the lookup happens only once.
3. `rand(RGB{Float32}, 1080, 1980)` used to be slow because `ColorTypes.jl` did not explicitly pass random number generators around internally, resulting in `1080*1980-1` unnecessary calls to `default_rng()`
