Skip to content

RNG should have thread-local seeding #87

Description

@kyonifer

Right now, all threads share the same global seed. This makes it difficult to reproduce results in multi-threaded code. If two threads are both grabbing random values at the same time, they will receive different segments of the rng stream depending on scheduling. A contrived example:

    var out1 = 0.0
    var out2 = 0.0
    val N = 100
    setSeed(1234)
    val t1 = thread {
        for (i in 0..N) {
            out1 += randn(2,2).elementSum()
        }
    }
    val t2 = thread {
        for (i in 0..N) {
            out2 += randn(2,2).elementSum()
        }
    }
    t1.join()
    t2.join()
    println(out1 > out2)

Because the two threads are racing for values from the RNG, they will grab different values from run to run, and the output will sometimes be true and sometimes false. Ideally, nothing about the above program would change and it would always produce the same result.

Probably the easiest way to fix this is to have the global setSeed call be used to generate thread-local seeds which will be used by thread-local rngs. On the JVM this could be done in a lock-free manner using AtomicInteger for change detection (the goal should be to prefer slowness when a seed changes since that is normally a rare event, and avoid synchronization during generation when possible), probably isn't necessary on js (which is single threaded), and requires #86 (and probably workers) on native.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions