Reply
Performance comparison of rand() and mt_rand()
Replies: 2   Views: 1673  Subscribers: 0

Posted by reece · 20-05-2012 - 05:38

Edited by reece · 12-11-2012 - 04:57
As per a discussion with Luc and Justin in a group chat, we were looking at the rand function and how effective it is in terms of performance.

As Luc has worked extensively with C and performance intensive C programs, we got into a discussion about which routine is better. 

Rand uses the libc rand according to php.net and mt_rand uses Mersenne Twister. Wanting to know which of the 2 was the better in terms of performance i wrote a small test case to find out.

mt_rand is purported to be 4 times faster than that of rand. However the results of the test seemed some what inconclusive, or least not as claimed.

What php has to say about mt_rand():

­
php.net
Many random number generators of older libcs have dubious or unknown characteristics and are slow. 
By default, PHP uses the libc random number generator with the rand() function. The mt_rand() function is a drop-in replacement for this. 
It uses a random number generator with known characteristics using the »  [URL="http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html"]Mersenne Twister­[/URL], 
which will produce random numbers four times faster than what the average libc rand() provides.­
In all tests it seems rand comes out faster than mt_rand, though marginally at that. The difference between the 2 is hardly noticeable in the results. Below is the test case i wrote, its probably not the prettiest code in the world, and if anyone would like to dispute the method or improve upon the code then they are more than welcome to do so. I used getrandmax() for both, instead of using mt_getrandmax() for mt_rand() so that both tests would use the same max values to ensure comparable tests in terms of how much work each would have to do seeing as mt_rand() has a larger max than rand() can take. I tested the sample on higher values, for more accurate results i would suggest a sample of 200,000 which should give a better spread of results. You can increase the number of times you repeat the sampling by adjusting the repeat variable. ­
<?php
error_reporting(-1);
error_reporting(E_ALL);

/**
 *
 * @Author Reece Fowell
 *
 */
class Test
{
	private $sample = 1000;
	private $repeat = 30;
	private $write_bin = false; // set to true to get the randon numbers in a bin file.
	
	private $results = array(
		'rand'		=> array('times' => array(), 'data' => array()),
		'mt_rand'	=> array('times' => array(), 'data' => array()),
	);

	public function microtime_float()
	{
		$mt = microtime(true);
		
		return $mt;
	}

	public function do_rand()
	{
		$time_start = $this->microtime_float();
	
		for($iter = 0; $iter < ($this->sample); $iter++)
		{
			$rand = rand(0, getrandmax());
			
			$this->results['rand']['data'][] = $rand;
		}

		$time_end = $this->microtime_float();
		$this->results['rand']['times'][] = ($time_end - $time_start);
	}

	public function do_mt_rand()
	{
		$time_start = $this->microtime_float();
	
		for($iter = 0; $iter < ($this->sample); $iter++)
		{
			$rand = mt_rand(0, getrandmax());
			
			$this->results['mt_rand']['data'][] = $rand;
		}
	
		$time_end = $this->microtime_float();
		$this->results['mt_rand']['times'][] = ($time_end - $time_start);
	}

	public function exec()
	{
		for ($iter = 0; $iter < ($this->repeat); $iter++)
		{
			$this->do_rand();
			$this->do_mt_rand();
		}
		
		foreach ($this->results as $key => $result)
		{
			$total = 0;
			
			for ($iter = 0; $iter < ($this->repeat); $iter++)		
			{
				$total += $result['times'][$iter];
			}
			
			$average = $total / 10;
			
			$this->results[$key]['average']	= $average;
			
			if ($this->write_bin)
			{
				if ($handle = fopen($key . '.bin', 'a'))
				{
					foreach($result['data'] as $data_key => $data)
					{
						if (fwrite($handle, serialize($data)) === FALSE)
						{
							echo 'ERROR: could not write to file.';
						
							break;
						}
					}
					fclose($handle);
				}
			}
		}
	}
	
	public function get_results()
	{
		return $this->results;
	}
	public function get_sample()
	{
		return $this->sample;
	}
	public function get_repeat()
	{
		return $this->repeat;
	}
}

$test = new Test();

$test->exec();
$results = $test->get_results();

//echo '<pre>' . print_r($results, true) . '</pre>'; die();
?> 
<html>
	<head>
		<style type="text/css">
			table {
				border: 1px solid grey;
			}
			th {
				border: 1px solid grey;				
			}
			td {
				border: 1px solid grey;
			}
		</style>
	</head>
	<body>
		Measures in Micro-seconds.
		<table>	
			<tr>
				<th></th>
				<?php foreach ($results as $key => $result): ?>
				<th><?php echo $key ?></th>
				<?php endforeach ?>
			</tr>
			
			<?php
			for ($iter = 0; $iter < ($test->get_repeat()); $iter++)
			{
				echo '<tr>';
				echo '<td>Sample ' . ($iter +1) . '</td>';
				foreach($results as $key => $result)
				{
					echo '<td>' . $result['times'][$iter] . '</td>';
				}
				echo '</tr>';
			}
			
			echo '<tr>';
			echo '<td>Average</td>';
			foreach ($results as $key => $result)
			{
				echo '<td>' . $result['average'] . '</td>';
			}
			echo '</tr>';
			?>
		</table>
	</body>
</html>­
Is my code/methodology at fault or are the results of this test contrary to the statements on php.net? Here you can read about ­rand()­ and ­mt_rand()­. You can turn out the write_bin property to get all the rand values for further testing in binary files which you can analyse.­

Posted by luc · 20-05-2012 - 15:36

Just to point out in the mt_rand() article comments this was pointed out back in 2008. I think what this revolves around is what they mean by "average".­

Posted by Justin · 21-05-2012 - 06:13

If you and some friends see a pig fly and take a picture of it, and laws of physics tell you it's not possible, then the justification leans towards the factual(and anecdotal) evidence which all participants have seen and recorded, which is that pigs can fly. These results are no different. Though somewhat inconclusive, setting the repeat variable to 30 or higher creates a substantial test case in which a significant difference is notable in the overall ratio. For me, 25 out of 30 samples showed 'rand' to be faster then 'mt_rand'. That's a 5:1 ratio. Clearly a valid assumption can be made with that. Also by setting the sample size to around 500,000 loops or higher seems to render a larger time difference in each sample. As if the aforementioned isn't enough, php.net goes as far as to say that the loser in our test is 4 times faster. THAT shows the obvious editorial error in there documentation unless we are misinterpreting what they call 'faster' as Luc said.­