Clojure's random functions currently use {{Math.random}} and related features, which makes them impossible to seed. This seems like an appropriate use of a dynamic var (compared to extra arguments), since library code that wants to behave randomly could transparently support seeding without any extra effort.
I propose {{(def ^:dynamic \*rand* (java.util.Random.))}} in {{clojure.core}}, and that {{rand}}, {{rand-int}}, {{rand-nth}}, and {{shuffle}} be updated to use {{\*rand*}}.
I think semantically this will not be a breaking change.
h2. Criterium Benchmarks
I did some benchmarking to try to get an idea of the performance implications of using a dynamic var, as well as to measure the changes to concurrent access.
The code used is at [
https://github.com/gfredericks/clj-1452-tests]; the raw output is in a comment.
{{rand}} is slightly slower, while {{shuffle}} is insignificantly faster. Using {{shuffle}} from 8 threads is insignificantly slower, but switching to a {{ThreadLocalRandom}} manually in the patched version results in a 2.5x speedup.
Running on my 8 core Linode VM:
||Benchmark||Clojure||Runtime mean||Runtime std dev||
|{{rand}}|1.6.0|61.3ns|7.06ns|
|{{rand}}|1.6.0 + {{\*rand\*}}|63.7ns|1.80ns|
|{{shuffle}}|1.6.0|12.9µs|251ns|
|{{shuffle}}|1.6.0 + {{\*rand\*}}|12.8µs|241ns|
|{{threaded-shuffling}}|1.6.0|151ms|2.31ms|
|{{threaded-shuffling}}|1.6.0 + {{\*rand\*}}|152ms|8.77ms|
|{{threaded-local-shuffling}}|1.6.0|N/A|N/A|
|{{threaded-local-shuffling}}|1.6.0 + {{\*rand\*}}|64.5ms|1.41ms|
*Approach:* create a dynamic var \*rand* and update {{rand}}, {{rand-int}}, {{rand-nth}}, and {{shuffle}} to use {{\*rand*}}
*Patch:* CLJ-1452.patch
*Screened by:*