-
-
Save sarciszewski/f7bd4c0358a44321787b to your computer and use it in GitHub Desktop.
| <?php | |
| function shitty_prng($bytes = 32) | |
| { | |
| $buf = ''; | |
| for ($i = 0; $i < $bytes; ++$i) { | |
| $buf .= chr(mt_rand(0, 255)); | |
| } | |
| } | |
| function better_prng($bytes = 32) | |
| { | |
| if (function_exists('mcrypt_create_iv')) { | |
| return mcrypt_create_iv(32, MCRYPT_DEV_URANDOM); | |
| } | |
| return openssl_random_pseudo_bytes(32); | |
| } |
| <?php | |
| require "functions.php"; | |
| $buf = ''; | |
| $tests = []; | |
| $start = microtime(true); | |
| for ($i = 0; $i < 100000; ++$i) { | |
| $buf = shitty_prng(); | |
| } | |
| $tests['mtrand'] = ( microtime(true) - $start ); | |
| $start = microtime(true); | |
| for ($i = 0; $i < 100000; ++$i) { | |
| $buf = better_prng(); | |
| } | |
| $tests['csprng'] = ( microtime(true) - $start ); | |
| var_dump($tests); |
| array(2) { | |
| ["mtrand"]=> | |
| float(0.97707104682922) | |
| ["csprng"]=> | |
| float(0.51490783691406) | |
| } | |
| array(2) { | |
| ["mtrand"]=> | |
| float(0.98253417015076) | |
| ["csprng"]=> | |
| float(0.50583696365356) | |
| } | |
| array(2) { | |
| ["mtrand"]=> | |
| float(1.0016939640045) | |
| ["csprng"]=> | |
| float(0.5266740322113) | |
| } |
I ran this through my simple PHP Benchmark library and got these results on PHP 5.5.20.
openssl_prng = 225800 iterations per second (Fastest)
mcrypt_prng = 107595 iterations per second
better_prng = 99619 iterations per second
shitty_prng = 51612 iterations per second (Slowest)
You can re-run the benchmark and see the comparison on my site.
Hey @sarciszewski, I only just saw this. I am co-author of the CSPRNG-in-core RFC for PHP 7.
https://github.com/lt/php-src/tree/rand-bytes
On a vanilla Linux install the random_bytes() function is comparable in speed to OpenSSL. OpenSSL still has the edge because (by default) it doesn't read directly from the systems sources of random all the time, it maintains it's own random pool and does a bunch of "stuff" for FIPS compliance, so it has an in-memory buffer it can draw on which gives it a bit more speed.
On FreeBSD and OpenBSD it absolutely blows OpenSSL out of the water.
Also worth noting, if you install and build against LibreSSL-portable on Linux, you're also going to get much faster speeds than regular OpenSSL due to it providing it's own arc4random functions.
Edit:
Also, mcrypt_create_iv() does not depend on the mcrypt library. It's just a wrapper around CryptGenRandom on Windows and /dev/*random on Linux. A lot of the performance hit is because it opens/closes the file descriptor for every call.
Edit 2:
I refuse to sign up to reddit. If you have any questions about how (CS)RNGs function in PHP, or why things are slower/faster, ping me here, come have a chat on SO (http://chat.stackoverflow.com/rooms/11/php), or fire an email to leight -> gmail
Hi @lt - just saw your message! Sorry about that. I'll jump into the chatroom.
Just tested on PHP7 minus mcrypt for reasons of it not being in my docker for PHP7 (I think it's deprecated)
array(3) {
["mtrand"]=> float(0.54768705368042)
["csprng"]=> float(0.20757412910461)
["openssl"]=> float(0.19972586631775)
}
I ran multiple times, looks like the numbers are pretty solid, very little difference very old quad core inside docker environment
Try again with random_bytes() instead of mcrypt:
function shitty_prng($bytes = 32)
{
$buf = '';
for ($i = 0; $i < $bytes; ++$i) {
$buf .= chr(mt_rand(0, 255));
}
}
function better_prng($bytes = 32)
{
if (function_exists('mcrypt_create_iv')) {
return mcrypt_create_iv($bytes, MCRYPT_DEV_URANDOM);
}
return openssl_random_pseudo_bytes($bytes);
}
function openssl_prng($bytes = 32)
{
return openssl_random_pseudo_bytes($bytes);
}
function best_prng($bytes = 32)
{
return random_bytes($bytes);
}
$buf = '';
$tests = [];
$start = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
$buf = shitty_prng();
}
$tests['mtrand'] = ( microtime(true) - $start );
$start = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
$buf = better_prng();
}
$tests['csprng'] = ( microtime(true) - $start );
$start = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
$buf = openssl_prng();
}
$tests['openssl'] = ( microtime(true) - $start );
$start = microtime(true);
for ($i = 0; $i < 100000; ++$i) {
$buf = best_prng();
}
$tests['best'] = ( microtime(true) - $start );
var_dump($tests);Four runs:
root@debian:~# php bench.php
array(4) {
["mtrand"]=>
float(0.48157906532288)
["csprng"]=>
float(0.30486798286438)
["openssl"]=>
float(0.15188217163086)
["best"]=>
float(0.28898501396179)
}
array(4) {
["mtrand"]=>
float(0.4787449836731)
["csprng"]=>
float(0.29729914665222)
["openssl"]=>
float(0.13547110557556)
["best"]=>
float(0.28169298171997)
}
array(4) {
["mtrand"]=>
float(0.4635169506073)
["csprng"]=>
float(0.3045289516449)
["openssl"]=>
float(0.13414788246155)
["best"]=>
float(0.28961706161499)
}
array(4) {
["mtrand"]=>
float(0.45156908035278)
["csprng"]=>
float(0.30080914497375)
["openssl"]=>
float(0.13886404037476)
["best"]=>
float(0.29005599021912)
}
That's the machine I ran this on.