Create a gist now

Instantly share code, notes, and snippets.

Microbenchmark of generator implementation
<?php
error_reporting(E_ALL);
function xrange($start, $end, $step = 1) {
for ($i = $start; $i < $end; $i += $step) {
yield $i;
}
}
class RangeIterator implements Iterator {
protected $start;
protected $end;
protected $step;
protected $key;
protected $value;
public function __construct($start, $end, $step = 1) {
$this->start = $start;
$this->end = $end;
$this->step = $step;
}
public function rewind() {
$this->key = 0;
$this->value = $this->start;
}
public function valid() {
return $this->value < $this->end;
}
public function next() {
$this->value += $this->step;
$this->key += 1;
}
public function current() {
return $this->value;
}
public function key() {
return $this->key;
}
}
function urange($start, $end, $step = 1) {
$result = [];
for ($i = $start; $i < $end; $i += $step) {
$result[] = $i;
}
return $result;
}
function testTraversable($name, callable $traversableFactory) {
$startTime = microtime(true);
foreach ($traversableFactory() as $value) {
// noop
}
echo $name, ' took ', microtime(true) - $startTime, ' seconds.', "\n";
}
function testVariants($count) {
testTraversable(
"xrange ($count)",
function() use($count) { return xrange(0, $count); }
);
testTraversable(
"RangeIterator ($count)",
function() use($count) { return new RangeIterator(0, $count); }
);
testTraversable(
"urange ($count)",
function() use($count) { return urange(0, $count); }
);
testTraversable(
"range ($count)",
function() use($count) { return range(0, $count); }
);
}
testVariants(1000000);
testVariants(10000);
testVariants(100);
xrange (1000000) took 0.14633202552795 seconds.
RangeIterator (1000000) took 0.5944938659668 seconds.
urange (1000000) took 0.28175210952759 seconds.
range (1000000) took 0.23163318634033 seconds.
xrange (10000) took 0.0015981197357178 seconds.
RangeIterator (10000) took 0.0065171718597412 seconds.
urange (10000) took 0.0028119087219238 seconds.
range (10000) took 0.0022850036621094 seconds.
xrange (100) took 7.8916549682617E-5 seconds.
RangeIterator (100) took 0.00010895729064941 seconds.
urange (100) took 5.5074691772461E-5 seconds.
range (100) took 8.082389831543E-5 seconds.
xrange (1000000) took 0.14681696891785 seconds.
RangeIterator (1000000) took 0.60637879371643 seconds.
urange (1000000) took 0.29368591308594 seconds.
range (1000000) took 0.22470617294312 seconds.
xrange (10000) took 0.0014379024505615 seconds.
RangeIterator (10000) took 0.0056979656219482 seconds.
urange (10000) took 0.002910852432251 seconds.
range (10000) took 0.0023188591003418 seconds.
xrange (100) took 5.1021575927734E-5 seconds.
RangeIterator (100) took 0.00014209747314453 seconds.
urange (100) took 5.4121017456055E-5 seconds.
range (100) took 4.3869018554688E-5 seconds.
xrange (1000000) took 0.14807891845703 seconds.
RangeIterator (1000000) took 0.60018301010132 seconds.
urange (1000000) took 0.28284788131714 seconds.
range (1000000) took 0.22018909454346 seconds.
xrange (10000) took 0.0013790130615234 seconds.
RangeIterator (10000) took 0.006242036819458 seconds.
urange (10000) took 0.0016648769378662 seconds.
range (10000) took 0.0019738674163818 seconds.
xrange (100) took 9.2029571533203E-5 seconds.
RangeIterator (100) took 0.00013899803161621 seconds.
urange (100) took 5.1975250244141E-5 seconds.
range (100) took 4.3153762817383E-5 seconds.
@TuningGuide

On a Mac OS 10.6.8 with Intel i5 Quadcore, MAMP, PHP5.5 I got:

xrange (1000000) took 6.64660000801 seconds.
RangeIterator (1000000) took 21.9801108837 seconds.
urange (1000000) took 1.44044995308 seconds.
range (1000000) took 0.779198884964 seconds.

Why is xrange aka generator that much slower than urange and range?

@levic

@TuningGuide - were you running a beta version of php?

Mac OS 10.9 i7 I get results broadly in line with those above:
xrange (1000000) took 0.31727600097656 seconds.
RangeIterator (1000000) took 1.3108851909637 seconds.
urange (1000000) took 0.75727391242981 seconds.
range (1000000) took 0.60222411155701 seconds.

@Besedin86

On Ubuntu 15.10/PHP 5.6.11 with Intel Core i3-4170 CPU @ 3.70GHz × 4 I got next results:

xrange        (1000000) took 0.39292001724243 seconds.
RangeIterator (1000000) took 1.4107999801636 seconds.
urange        (1000000) took 0.24663400650024 seconds.
range         (1000000) took 0.16190481185913 seconds.
xrange        (10000) took 0.0039498805999756 seconds.
RangeIterator (10000) took 0.013652801513672 seconds.
urange        (10000) took 0.0020461082458496 seconds.
range         (10000) took 0.0010190010070801 seconds.
xrange        (100) took 5.1021575927734E-5 seconds.
RangeIterator (100) took 0.00014996528625488 seconds.
urange        (100) took 2.288818359375E-5 seconds.
range         (100) took 1.3113021850586E-5 seconds.

So that range or xrange is more preffered

@dkarvounaris
dkarvounaris commented Apr 24, 2016 edited

Tested it on several servers (Intel Core i7-2600, Xeon E3-1230, Xeon E5-2620 and some more) and depending on what the underlying system is, results can be different. On some systems, xrange will be faster than urange and range, on others range will require only about 1/3 of the time that xrange does. This is with PHP 5.5 and 5.6, which seems to be not unique with the same version either.

Differences will be on a fast server between xrange, urange and range generally insignificant, with lower memory usage/less iterations.

On PHP7 however (on an Intel Xeon), which uses less memory and is highly optimized, urange and range are always the big winners, even with the memory usage caused by 10M iterations, with xrange being 3 times slower than urange and being about 15 times slower than xrange. These are the results from this one test:

xrange        (1000000) took 0.64220499992371 seconds.
RangeIterator (1000000) took 2.6198852062225 seconds.
urange        (1000000) took 0.19365906715393 seconds.
range         (1000000) took 0.043736934661865 seconds.
xrange        (10000) took 0.0069031715393066 seconds.
RangeIterator (10000) took 0.026669025421143 seconds.
urange        (10000) took 0.001878023147583 seconds.
range         (10000) took 0.00037002563476562 seconds.
xrange        (100) took 0.00013208389282227 seconds.
RangeIterator (100) took 0.00035595893859863 seconds.
urange        (100) took 3.9100646972656E-5 seconds.
range         (100) took 4.0054321289062E-5 seconds.
xrange        (10000000) took 6.6685261726379 seconds.
RangeIterator (10000000) took 26.136057853699 seconds.
urange        (10000000) took 1.948203086853 seconds.
range         (10000000) took 0.43352198600769 seconds.
@dkarvounaris
dkarvounaris commented Apr 24, 2016 edited

Just for comparison, the same benchmark on the same system, but with PHP 5.6.19 instead 7.0:

xrange        (1000000) took 0.84735608100891 seconds.
RangeIterator (1000000) took 2.6602609157562 seconds.
urange        (1000000) took 0.60670304298401 seconds.
range         (1000000) took 0.34244179725647 seconds.
xrange        (10000) took 0.010324001312256 seconds.
RangeIterator (10000) took 0.027228832244873 seconds.
urange        (10000) took 0.0047950744628906 seconds.
range         (10000) took 0.0020461082458496 seconds.
xrange        (100) took 0.00014185905456543 seconds.
RangeIterator (100) took 0.00033211708068848 seconds.
urange        (100) took 8.6069107055664E-5 seconds.
range         (100) took 7.1048736572266E-5 seconds.
xrange        (10000000) took 8.7790288925171 seconds.
RangeIterator (10000000) took 26.90533208847 seconds.
urange        (10000000) took 6.6500968933105 seconds.
range         (10000000) took 3.7362380027771 seconds.

One can say by these numbers, if you want to make sure, your code is on PHP7 as slow as on PHP5, then make sure you use Iterator, which is performing equally bad on both versions!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment