4 ms·
This reminds me of the fact that Go 1.20 added a clever optimization: If you call `rand.Seed` to seed the global RNG, it's assumed that you want it to produce a
by teraflop 2y ago
This reminds me of the fact that Go 1.20 added a clever optimization: If you call `rand.Seed` to seed the global RNG, it's assumed that you want it to produce a single deterministic sequence across all threads/goroutines, which requires locking. But if you never explicitly seed it, you get a different implementation that uses thread-local RNG state without locking.
(In practice, there is no way for a program to distinguish this from a single RNG that was initialized with a truly random seed that you can't recover, so it doesn't break backwards compatibility.)
Unfortunately, the C standard prescribes that failing to call `srand` must be equivalent to calling `srand(1)`, so I don't think glibc can get away with this trick.
Mentioned here: https://go.dev/blog/randv2 https://go.dev/blog/randv2
- skissane 2y ago> Unfortunately, the C standard prescribes that failing to call `srand` must be equivalent to calling `srand(1)`, so I don't think glibc can get away with this trick. I think it could. Just supply two implementations of rand(): a strictly conforming one without this optimisation, and a less than strictly conforming one with it. The second is implemented in a function with a name like `__gnu_rand_optimized`, and then if you `#define __GNU_RAND_OPTIMIZED` before `#include <stdlib.h>`, then that header does `#define rand __gnu_rand_optimized`. All the C standard requires is that you provide a mode of operation which strictly conforms to it. A platform is allowed to provide configuration options which cause it to be violated in various ways. Strictly conforming code won't turn on those options and so will work as the standard prescribes. If you choose to turn on such an option, that's a non-portable extension, and how it works is between you and the platform, the C standard isn't involved.
- rerdavies 2y agoAdd a new function that modifies behavior of an existing one. Or add a new function that provides behavior different from the existing one. The latter is simpler. And safer. And more flexible. And faster, because you don't have a condition to check.
- vlovich123 2y agoI don’t see why it requires locking. You could easily have a TLS seed that gets initialized from the main thread RNG that’s seeded with Rand.seed. No locking required. It’s not like reading from a single RNG state from multiple thread with locks is any extra deterministic over that approach since you fundamentally would have to synchronize at a higher level to guarantee that the RNG is read deterministically across all thread orderings.
- yuliyp 2y agoI think the point is that the library probably should assume that if you called `Rand.seed` and/or `srand` it's because you intended to get a specific sequence of random numbers out of it.
- vlovich123 2y agoWhat did I write that suggests you wouldn’t get a deterministic specific sequence equivalent to a lock within the RNG? What I said is if you fail to synchronize the surrounding code’s order of calls into the RNG, then you’re going to have non-determinism anyway even with locks within the RNG. For example: T1: If rng() < 0.5 { Rng() } T2: Rng() The specific numbers returned by calls to rng() depend on the thread ordering UNLESS the user carefully has higher level synchronization themselves to guarantee the ordering. This shows that the locking within the RNG is insufficient and the better performing option that maintains equivalent invariants should be selected (i.e. initialize the TLS RNG state for the new thread from a call to read entropy from the current thread’s RNG state).
- yuliyp 2y agoI'm trying to figure out how you switch "into" thread-local prng mode after a call to srand. Case 1: T1: srand(); rng(); rng() T2: no calls to rng Case 2: T1: srand(); T2: (spawns after the srand call) rng(); rng() How would T2 know that it should produce the seeded sequence as opposed to one based on its own prng?
- 2y ago
- codr7 2y agoExactly my issue with Go. For a language that talks so much about simplicity, it puts a lot of effort into inventing clever wheels, with mixed results. I recently realized that SQL connections are thread safe and automagically pooled, I would have preferred more manual control. Don't get me started on date/time-formats.