[CLJ-1420] ThreadLocalRandom instead of Math/random Created: 11/May/14 Updated: 20/Jun/14
|Affects Version/s:||Release 1.7|
Requires Java >=1.7!
The standard Math.random() is thread-safe through being declared as a synchronized static method.
The patch uses java.util.concurrent.ThreadLocalRandom which actually seems to be two times faster than the ordinary Math.random() in a simple single threaded criterium.core/bench:
The reason I investigated the function at all was to be sure random-number generation was not a bottleneck when performance testing multithreaded load generation.
If necessary, one could of course make a conditional declaration (like in fj-reducers) based on the existence of the class java.util.concurrent.ThreadLocalRandom, if Clojure 1.7 is to be compatible with Java versions < 1.7
|Comment by Linus Ericsson [ 11/May/14 11:05 AM ]|
Benchmark on current rand (clojure 1.6.0), $ java -version
:jvm-opts ^:replace  (ie no arguments to the JVM)
(bench (rand 10))
Found 1 outliers in 60 samples (1.6667 %)
(bench (rand 10))
Found 2 outliers in 60 samples (3.3333 %)
I had similar results on Clojure 1.6.0, and ran several different tests with similar results. java.util.Random.nextInt is suprisingly bad. The ThreadLocalRandom version of .nextInt is better, but rand-int can take negative integers, which would lead to some argument conversion for (.nextInt (ThreadLocalRandom/current) n) since it need upper and lower bounds instead of a simple multiplication of a random number [0,1).
The (.nextDouble (ThreadLocalRandom/current) argument) is very quick, but cannot handle negative arguments. The speed given a plain multiplication is about 30 ns.
|Comment by Linus Ericsson [ 11/May/14 12:44 PM ]|
Added some simplistic tests to be sure that rand and rand-int accepts ratios, doubles and negative numbers of various kinds. A real test would likely include repeated generative testing, these tests are mostly for knowing that various arguments works etc.
|Comment by Linus Ericsson [ 11/May/14 1:38 PM ]|
0001-rand-using-ThreadLocalRandom-and-tests-for-random.patch contains the changed (rand) AND the test cases.
|Comment by Alex Miller [ 11/May/14 5:45 PM ]|
Clojure requires Java 1.6.0 so this will need to be reconsidered at a later date. We do not currently have any plans to bump the minimum required JDK in Clojure 1.7 although that could change of course.
|Comment by Gary Fredericks [ 11/May/14 5:54 PM ]|
I've always thought that the randomness features in general are of limited utility due to the inability to seed the PRNG, and that a clojure.core/rand dynamic var would be a reasonable way to do that.
Maybe both of these problems could be partially solved with a standard library? I started one at https://github.com/fredericksgary/four, but presumably a contrib library would be easier for everybody to standardize on.
|Comment by Linus Ericsson [ 12/May/14 2:17 AM ]|
Gary, I'm all for creating some well-thought out random-library, which could be a candidate for some library clojure.core.random if that would be useful.
Please have a look at http://code.google.com/p/javarng/ since that seems to do what you library four does (and more). Probably we could salvage either APIs, algorithms or both from this library.
I'll contact you via mail!
|Comment by Gary Fredericks [ 20/Jun/14 10:21 AM ]|
Come to think of it, a rand var in clojure.core shouldn't be a breaking change, so I'll just make a ticket for that to see how it goes. That should at the very least allow solving the concurrency issue with binding. The only objection I can think of is perf issues with dynamic vars?
|Comment by Gary Fredericks [ 20/Jun/14 10:42 AM ]|
New issue is at CLJ-1452.