Skip to content
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

random sampling from an (abstract)array is slow #20582

Closed
CarloLucibello opened this issue Feb 12, 2017 · 8 comments · Fixed by #29240
Closed

random sampling from an (abstract)array is slow #20582

CarloLucibello opened this issue Feb 12, 2017 · 8 comments · Fixed by #29240
Labels
collections Data structures holding multiple items, e.g. sets performance Must go faster potential benchmark Could make a good benchmark in BaseBenchmarks randomness Random number generation and the Random stdlib

Comments

@CarloLucibello
Copy link
Contributor

CarloLucibello commented Feb 12, 2017

It is three times slower than this alternative implementation

julia> myrand(v) = (i = ceil(Int,rand()*length(v));  v[i])

julia> @benchmark rand(1:100)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     36.048 ns (0.00% GC)
  median time:      36.996 ns (0.00% GC)
  mean time:        37.104 ns (0.00% GC)
  maximum time:     66.210 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     993
  time tolerance:   5.00%
  memory tolerance: 1.00%

julia> @benchmark myrand(1:100)
BenchmarkTools.Trial: 
  memory estimate:  0 bytes
  allocs estimate:  0
  --------------
  minimum time:     11.824 ns (0.00% GC)
  median time:      12.332 ns (0.00% GC)
  mean time:        12.286 ns (0.00% GC)
  maximum time:     35.627 ns (0.00% GC)
  --------------
  samples:          10000
  evals/sample:     999
  time tolerance:   5.00%
  memory tolerance: 1.00%
@ararslan ararslan added collections Data structures holding multiple items, e.g. sets performance Must go faster labels Feb 13, 2017
@StefanKarpinski
Copy link
Member

cc @rfourquet, who made sure this stuff was all very fast at one point. We should add BaseBenchmarks for this so that it can't regress again. Thanks for reporting!

@tkelman tkelman added the potential benchmark Could make a good benchmark in BaseBenchmarks label Feb 13, 2017
@martinholters
Copy link
Member

Probably ceil(Int,rand()*length(v)) is a bad way to get a uniform distribution between 1 and length(v)? It might be faster than what we have, but might be biased?

@CarloLucibello
Copy link
Contributor Author

Probably ceil(Int,rand()*length(v)) is a bad way to get a uniform distribution between 1 and length(v)? It might be faster than what we have, but might be biased?

Yes, I have to delve deeper into this, but it could be slightly unbiased, with a negligible bias for small length(v) wich gets higher for bigger vectors

@StefanKarpinski
Copy link
Member

That version is also slightly buggy since there's a small chance of getting a zero index and then getting a bounds error upon indexing.

@bkamins
Copy link
Member

bkamins commented Aug 31, 2018

This issue is recurring many times on Discourse (https://discourse.julialang.org/t/rand-1-10-vs-int-round-10-rand/14339/9), so I put some benchmarks.

Here is the code that shows where we have a problem:

# tweak function to make rand(range) return number of loop iterations
function Random.rand(rng::AbstractRNG, sp::Random.LessThan)
    i = 0
    while true
        i += 1
        x = rand(rng, sp.s)
        x <= sp.sup && return i-1
    end
end

And now running it for small ranges gives:

julia> [mean(rand(1:j) for i in 1:10^6) for j in 1:16]
16-element Array{Float64,1}:
 1.0
 1.0
 1.333166
 1.0
 1.600809
 1.333687
 1.142155
 1.0
 1.777679
 1.599978
 1.455112
 1.333272
 1.231023
 1.143564
 1.067084
 1.0

I guess it cannot be helped without using div/mod which is more expensive than generating an additional pseudorandom number (@rfourquet did I get your idea in this design right?).

And now the issue is that for a smal n the formula 1+floor(Int,n*rand()) has a small bias. For example for n=10^6 it is around 10^-10. Now, in order to detect this difference statistically the number of required samples is astronomical, so:

Maybe we want to allow for some small bias in trade off for speed for small n? If not in rand then maybe in something like fastrand? OTOH maybe it is better to do such things in packages. Not sure.

@rfourquet
Copy link
Member

@rfourquet did I get your idea in this design right?

Indeed, that was the idea of the change in #27560, as generation with MersenneTwister is fast enough that this approach is usually more performant than using div/mod. That said, the default Sampler for other RNGs is still using div/mod (SamplerRangeInt).

@rfourquet
Copy link
Member

I think that rather than fastrand, we could create an object Biased (or Fast), so that a call would look like rand(Biased(1:10)). Like that the rand infrastructure is still available (e.g. we don't have to reimplement array generation for fastrand).

@bkamins
Copy link
Member

bkamins commented Aug 31, 2018

Nice idea. I think that Fast would be nice, and we could avoid exporting it so user would call it using Random.Fast (like with seed!) as I guess this operation will not be so common.

rfourquet added a commit that referenced this issue Aug 31, 2018
This uses a faster method than in rand(a:b), which can be biased,
depending on the length of a:b.
StefanKarpinski added a commit that referenced this issue May 1, 2020
* implement "nearly division less" algorithm for rand(a:b)

Cf. https://arxiv.org/abs/1805.10941.
Closes #20582, #29004.

* fix overflow error in tests

* make NDL the default algo

* update NEWS.md

* try make tests pass on 32-bits machines

* add a comment for mod(-s, s)

* remove vestigial transient `fast` function, and update comments
@rfourquet rfourquet added the randomness Random number generation and the Random stdlib label May 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
collections Data structures holding multiple items, e.g. sets performance Must go faster potential benchmark Could make a good benchmark in BaseBenchmarks randomness Random number generation and the Random stdlib
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants