svaarala / duktape

Duktape - embeddable Javascript engine with a focus on portability and compact footprint
MIT License
5.92k stars 514 forks source link

Improve built-in PRNG quality #971

Open svaarala opened 7 years ago

svaarala commented 7 years ago

Current implementation is xoroshiro128+ with seed derived from Date.now() and duk_heap pointer, premixed using SplitMix64. The main shortcoming of this setup is that the seed is very similar when creating multiple Duktape heaps. There's also no way to explicitly provide a seed from user code (although the entire PRNG provider can be changed).

Also if two Duktape heaps are created at exactly the same moment and they get the same duk_heap pointer (for example because they are two separate processes with the same virtual address layout) the same PRNG sequence can be generated.

There's some discussion of various issues and alternatives in #970.

fatcerberus commented 7 years ago

My instinct is that it's probably not worth it to provide a method for manual seeding as such to the application; by the time an application needs to do this it'd make more sense just to swap out the whole RNG, which is already possible now.

What might be worth pursuing, however, is to expose the xoroshiro128+ state through the API. Then an application can save and restore the state (potentially useful for repeatable tests), or even modify it directly for manual mixing--XOR'ing in values every once in a while, for example. One roadblock for this is that xoroshiro128+ isn't used on all targets yet.

svaarala commented 7 years ago

I wouldn't want to expose the specific algorithm via the API. It's not impossible that it would change again later on.

svaarala commented 7 years ago

@fatcerberus By the way, note that the ability to save/restore the internal PRNG state does not mean Math.random() output could be in effect rewound/restarted. The reason for this is that the internal PRNG is also used for internal purposes, currently for Array.prototype.sort() random pivot selection. So if any sort() calls are done, that will also affect the Math.random() sequence. The random state is also shared across all threads in the same Duktape heap which makes the individual Math.random() sequences in each thread potentially unpredictable. This is quite intentional right now, and it's quite likely some other internals will need access to randomization at some point.

So applications desiring a predictable, controllable Math.random() stream should replace Math.random() or use a separate random number binding entirely, and not just replace the internal provider.

svaarala commented 7 years ago

In this sense the Ecmascript randomization API is quite a barebones one. It'd be a pretty useful API to be able to create multiple PRNG sources and control who draws data from them. But that's not the case for the default built-in. On the other hand one strength of the Ecmascript built-in bindings is that they're simple and there's very little "provider factory" thinking :)

fatcerberus commented 7 years ago

I actually implemented such a system in minisphere where games can create individual RNG streams and manage their states independently:

https://github.com/fatcerberus/minisphere/blob/master/docs/sphere2-api.txt#L243-L288

svaarala commented 7 years ago

Looks useful, that's something I'd also prefer if my application required a controlled PRNG source. It's somewhat unfortunately that Duktape can't easily help with that, at least without introducing custom Ecmascript or C bindings :-/.

trafalmuffti commented 3 years ago

I generally use current time and process ID for seeding RNGs, for my C projects.