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

Activity

Show:
Alex Miller
December 6, 2018, 6:27 PM

Right

gfredericks
December 6, 2018, 3:20 PM

The >=JDK7 aspect of ThreadLocalRandom is no longer a problem, correct?

Ghadi Shayban
January 14, 2016, 4:15 PM

Just noting, ThreadLocalRandom is >= JDK 7.

Stuart Halloway
July 19, 2015, 1:42 PM

workaround: data.generators provides seedable random

gfredericks
December 30, 2014, 11:44 PM

Also worth noting that (as I did in the benchmark code) with just the patch's changes (i.e., no ThreadLocal involved) users still gain the ability to do ThreadLocal manually, which is not currently possible.

Your pinned fields
Click on the next to a field label to start pinning.

Assignee

Unassigned

Reporter

gfredericks

Labels

Approval

Triaged

Patch

Code and Test

Priority

Major

Affects versions