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

math/rand: rand.Float64() twice slower since Dec 18 #7267

Closed
siritinga opened this issue Feb 5, 2014 · 5 comments
Closed

math/rand: rand.Float64() twice slower since Dec 18 #7267

siritinga opened this issue Feb 5, 2014 · 5 comments
Milestone

Comments

@siritinga
Copy link

Hello everyone, 

I have noticed that the float random number generator in Go tip is slower than 1.2. I
have done some testing and most of the float functions of math/rand are slightly slower,
but Float64 is much slower, it takes twice the time. Benchmark here:
http://play.golang.org/p/_bjHfAtRv3

Checking the code, it seems than the problem appears in revision 3ed9d5c72102 in file
math/rand/rand.go, in particular:

changeset:   18661:3ed9d5c72102
user:        Jeff R. Allen <jra@nella.org>
date:        Wed Dec 18 15:38:53 2013 -0500
summary:     math/rand: Float32/64 must only return values in [0,1)

Diff for Float64:
 // Float64 returns, as a float64, a pseudo-random number in [0.0,1.0).
-func (r *Rand) Float64() float64 { return float64(r.Int63()) / (1 << 63) }
+func (r *Rand) Float64() float64 { return float64(r.Int63n(1<<53)) / (1 <<
53) }

Discussed here:
https://groups.google.com/forum/#!searchin/golang-dev/rand.Float64/golang-dev/3b_OW2DYpbI/wuePqYv-JvsJ

After this change, Float64() uses Int63n() that is much slower than Int63. There is a
similar change for Float32(), using Int31n(), but Int31n is fast enough.

If it is possible to change it back somehow to Int63, speed should return to previous
values.

Maybe in a 64 bit system the difference is not noticeable. I have only tested in arm and
linux/386.

There is also a side effect: a random sequence of Floats will be different than in
previous Go versions, even if the same seed is used. That may affect the repeatability
of some tests (I don't think that would break the compatibility promise, but...)

(GO 1.2) $ go test -bench=.
BenchmarkFloat64         1000000              1211 ns/op
BenchmarkFloat32         1000000              1280 ns/op
BenchmarkExpFloat64      1000000              1184 ns/op
BenchmarkNormFloat64     1000000              1182 ns/op
BenchmarkInt     2000000               880 ns/op
BenchmarkIntn    1000000              1291 ns/op
BenchmarkInt31n  1000000              1248 ns/op
BenchmarkInt63n  1000000              2194 ns/op
BenchmarkInt31   2000000               873 ns/op
BenchmarkInt63   2000000               834 ns/op

(GO TIP) $ ~/gotip/go/bin/go version
go version devel +d067c6de4ec3 Wed Feb 05 01:24:51 2014 -0500 linux/arm
$ ~/gotip/go/bin/go test -bench=.
BenchmarkFloat64          500000              2646 ns/op
BenchmarkFloat32         1000000              1391 ns/op
BenchmarkExpFloat64      1000000              1196 ns/op
BenchmarkNormFloat64     1000000              1211 ns/op
BenchmarkInt     2000000               876 ns/op
BenchmarkIntn    1000000              1339 ns/op
BenchmarkInt31n  1000000              1271 ns/op
BenchmarkInt63n  1000000              2419 ns/op
BenchmarkInt31   2000000               874 ns/op
BenchmarkInt63   2000000               818 ns/op
@siritinga
Copy link
Author

Comment 1:

Maybe something like this could do the trick 
// unchanged from 1.2. A direct cast to float32 may give 1.0, but should a cast preserve
the [0,1) condition? Something like the line below cold be done.
func (r *Rand) Float64() float64 { return float64(r.Int63()) / (1 << 63) } 
//All random bits preserved and it won't be 1.0. Value 1-1.e-7 may need some tweak
func (r *Rand) Float32() float32 { return float32(f64*(1-1.e-7)) } 
Example code:
http://play.golang.org/p/fliCIqAVHT

@ianlancetaylor
Copy link
Contributor

Comment 2:

Labels changed: added repo-main, release-go1.3maybe.

@siritinga
Copy link
Author

Comment 3:

Maybe something like this could do the trick 
// Using Int63. The value 1.-1.e-16 may need some tweak
func (r *Rand) Float64() float64 { return float64(r.Int63()) / (1 << 63) *
(1.-1.e-16) } 
// Value 1-1.e-7 may need some tweak
func (r *Rand) Float32() float32 { return float32(r.Float64() * (1.-1.e-7))} 
Example code:
http://play.golang.org/p/YeRP-2xEpK

@rsc
Copy link
Contributor

rsc commented Mar 3, 2014

Comment 4:

https://golang.org/cl/69980047

Status changed to Started.

@rsc
Copy link
Contributor

rsc commented Mar 4, 2014

Comment 5:

This issue was closed by revision 1a936eb.

Status changed to Fixed.

@rsc rsc added this to the Go1.3 milestone Apr 14, 2015
@golang golang locked and limited conversation to collaborators Jun 25, 2016
This issue was closed.
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants