clojure.core/*rand* for seedable randomness

Description

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.

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:

Environment

None

Assignee

Unassigned

Reporter

gfredericks

Labels

Approval

Triaged

Patch

Code and Test

Affects versions

Priority

Major
Configure