rfc:object_scope_prng
Differences
This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
rfc:object_scope_prng [2021/01/26 17:46] zeriyoshi docment 2.0 |
rfc:object_scope_prng [2021/04/14 14:38] zeriyoshi status update |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== PHP RFC: Object scoped RNG Implementations. ====== | ====== PHP RFC: Object scoped RNG Implementations. ====== | ||
- | * Version: 2.0 (Implementation: | + | * Version: 2.1 (Implementation: |
- | * Date: 2021-01-27 | + | * Date: 2020-12-20 |
* Author: Go Kudo < | * Author: Go Kudo < | ||
- | * Status: | + | * Status: |
* Implementation: | * Implementation: | ||
* First Published at: https:// | * First Published at: https:// | ||
Line 12: | Line 12: | ||
<code php> | <code php> | ||
\mt_srand(1234); | \mt_srand(1234); | ||
- | echo \mt_rand(); // Generate random number. | + | \mt_rand(); // Generate random number. |
</ | </ | ||
Line 50: | Line 50: | ||
<code shell> | <code shell> | ||
$ git clone " | $ git clone " | ||
- | $ time php -r ' | + | $ time ./php -r ' |
real 0m0.745s | real 0m0.745s | ||
Line 74: | Line 74: | ||
user 0m0.010s | user 0m0.010s | ||
sys | sys | ||
+ | </ | ||
+ | |||
+ | RFC has been passed and Fiber will be introduced in PHP 8.1. Implementations with unpredictable execution order, such as Fiber, make this problem worse. | ||
+ | For example (with amphp), the following results are difficult for the user to be aware of and are not guaranteed to be consistent in the future. | ||
+ | |||
+ | <code php> | ||
+ | use function Amp\defer; | ||
+ | use function Amp\delay; | ||
+ | |||
+ | function task(int $i): void { | ||
+ | global $running; | ||
+ | |||
+ | while ($running) { | ||
+ | delay(250); | ||
+ | echo "${i} : " . mt_rand() . " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | mt_srand(1234); | ||
+ | |||
+ | $running = true; | ||
+ | |||
+ | defer(fn() => task(1)); | ||
+ | defer(fn() => task(2)); | ||
+ | defer(fn() => task(3)); | ||
+ | defer(fn() => task(4)); | ||
+ | |||
+ | delay(1000); | ||
+ | $running = false; | ||
+ | |||
+ | /* | ||
+ | Result: | ||
+ | 1 : 411284887 | ||
+ | 4 : 1068724585 | ||
+ | 2 : 1335968403 | ||
+ | 3 : 1756294682 | ||
+ | 1 : 940013158 | ||
+ | 3 : 1314500282 | ||
+ | 4 : 1686544716 | ||
+ | 2 : 1656482812 | ||
+ | 1 : 1674985287 | ||
+ | 2 : 1848274264 | ||
+ | 3 : 585388171 | ||
+ | 4 : 323490420 | ||
+ | 4 : 593702477 | ||
+ | 3 : 426315791 | ||
+ | 2 : 1722007381 | ||
+ | 1 : 1750549071 | ||
+ | */ | ||
</ | </ | ||
Line 83: | Line 132: | ||
Implements class-based object scoped PRNG into PHP. | Implements class-based object scoped PRNG into PHP. | ||
- | First, implement the following interface. This is a hypothetical PHP code. | + | First, implement the following interface. This is a hypothetical PHP code.' |
+ | |||
+ | Basically, this interface are supposed to be used as arguments to one of the functions. In other words, the next() and next64() methods are not intended to be called directly. next64() returns an invalid value in a 32-bit environment. | ||
<code php> | <code php> | ||
Line 105: | Line 156: | ||
namespace RNG; | namespace RNG; | ||
- | class XorShift128Plus implements RNGInterface | + | class XorShift128Plus implements RNGInterface // Fast modern PRNG. |
- | class MT19937 implements RNGInterface | + | { |
- | class OS implements RNGInterface | + | public function __construct(int $seed) {} |
+ | public function next(): int {} | ||
+ | public function next64(): int {} | ||
+ | public function __serialize(): | ||
+ | public function __unserialize(array $data): void {} | ||
+ | } | ||
+ | |||
+ | class MT19937 implements RNGInterface // Completely consistent \mt_srand() and \mt_rand() implementation. | ||
+ | { | ||
+ | public function __construct(int $seed) {} | ||
+ | public function next(): int {} | ||
+ | public function next64(): int {} | ||
+ | public function __serialize(): | ||
+ | public function __unserialize(array $data): void {} | ||
+ | } | ||
+ | |||
+ | class OS implements RNGInterface | ||
+ | { | ||
+ | public function next(): int {} | ||
+ | public function next64(): int {} | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Some RNG implementations can serialize state using the standard PHP serialization methods (serialize() and unserialize() function). This is useful for the purpose of storing state. | ||
+ | |||
+ | <code php> | ||
+ | $rng = new RNG\XorShift128Plus(1234); | ||
+ | |||
+ | \rng_next($rng); | ||
+ | |||
+ | $serialized_string = \serialize($rng); | ||
+ | |||
+ | $rng2 = \unserialize($serialized_string); | ||
+ | |||
+ | \rng_next($rng) === rng_next($rng2); | ||
</ | </ | ||
Line 121: | Line 206: | ||
Finally, implement a function that can generate values from RNGs, such as the mt_rand(), random_bytes() function. If the $unsigned argument is false, it will return the value generated by the RNG as is. | Finally, implement a function that can generate values from RNGs, such as the mt_rand(), random_bytes() function. If the $unsigned argument is false, it will return the value generated by the RNG as is. | ||
- | |||
- | The rng_next64() function will throw a ValueError exception if 64-bit integer is not available on the platform. | ||
<code php> | <code php> | ||
Line 133: | Line 216: | ||
/** @throws ValueError */ | /** @throws ValueError */ | ||
function rng_next64(RNG\RNGInterface $rng, bool $unsigned = true): int {} // Generates 64bit random number | function rng_next64(RNG\RNGInterface $rng, bool $unsigned = true): int {} // Generates 64bit random number | ||
+ | </ | ||
+ | |||
+ | The rng_next64() function will throw a ValueError exception if 64-bit integer is not available on the platform. | ||
+ | |||
+ | This is for proper handling of next64() in a 32-bit environment and to improve interoperability with the mt_rand() function. The mt_rand() function performs a bit shift on the result and always returns an unsigned integer. | ||
+ | |||
+ | <code php> | ||
+ | // If the second argument is true (default), rng_next() performs bit shifting like mt_rand() and always returns an unsigned integer. | ||
+ | \mt_srand(1234); | ||
+ | $mt_state = new \RNG\MT19937(1234); | ||
+ | \mt_rand() === \rng_next($mt_state); | ||
+ | |||
+ | // If false, it will return the value as is. This is exactly the same result as $rng-> | ||
+ | \mt_rand() === \rng_next($mt_state, | ||
+ | |||
+ | // This is useful if you want to use the numbers generated by the RNG directly. | ||
+ | \rng_next(new \RNG\MT19937(1234), | ||
</ | </ | ||
Line 175: | Line 275: | ||
* mt_rand() | * mt_rand() | ||
- | ===== Proposed Voting Choices | + | ===== Vote ===== |
- | Yes/No, requiring | + | Voting opens 2021-04-01 and 2021-04-15 at 00:00:00 EDT. 2/3 required to accept. |
- | There are a few additional options for implementation. | + | <doodle title=" |
- | + | ||
- | <doodle title=" | + | |
* Yes | * Yes | ||
* No | * No | ||
- | </ | ||
- | |||
- | <doodle title=" | ||
- | * Top Level (\RNGInterface, | ||
- | * " | ||
- | * " | ||
</ | </ | ||
===== Patches and Tests ===== | ===== Patches and Tests ===== | ||
* https:// | * https:// |
rfc/object_scope_prng.txt · Last modified: 2021/04/14 14:38 by zeriyoshi