Skip to content

Instantly share code, notes, and snippets.

@krakjoe
Last active September 11, 2020 17:05
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save krakjoe/8444591 to your computer and use it in GitHub Desktop.
arrayof-perf.md

Arrayof Performance

<?php
function arrayof_foo(Foo[] $fooze) {
    foreach ($fooze as $foo) {
    	$foo->bar();
    }
}

function user_instanceof_foo(array $fooze) {
    foreach ($fooze as $foo) {
        if ($foo instanceof Foo) {
            $foo->bar();
        }
    }
}

function user_filter_foo(array $fooze) {
    foreach (array_filter($fooze, function ($object) {
		return $object instanceof Foo;
	}) as $foo) {
        $foo->bar();
    }
}

function user_arrays(array $arrays) {
	foreach ($arrays as $array) {
		if (is_array($array)) {
			continue;
		}
	}
}

function arrayof_arrays(array[] $arrays) {
	foreach ($arrays as $array) {
		continue;
	}
}

function user_callables(array $callables) {
	foreach ($callables as $callable) {
		if (is_callable($callable)) {
			$callable();
		}
	}
}

function arrayof_callables(callable[] $callables) {
	foreach ($callables as $callable) {
		$callable();
	}
}

function user_interfaces(ArrayOfFoo $fooArray) {
	foreach ($fooArray as $i => $foo) {
		$foo->bar();
	}
}

abstract class ArrayOf implements ArrayAccess, Iterator, Countable {
	protected $type = null;
 
	protected $data = [];
 
	protected $index = 0;
 
	public function __construct() {
		if (is_null($this->type)) { throw new Exception('Extending classes must specify a type.'); }
	}
 
	public function offsetSet($offset, $value) {
		if ($value instanceof $this->type) {
			$this->data[$offset] = $value;
		} else {
			$type = is_object($value) ? get_class($value) : gettype($value);
			throw new UnexpectedValueException(
				sprintf('Value must be of type %s, %s given.', $this->type, $type)
			);
		}
	}
	
	public function offsetExists($offset) { return isset($this->data[$offset]); }
	public function offsetGet($offset) { return $this->data[$offset]; }
	public function offsetUnset($offset) { unset($this->data[$offset]); }
	public function current() { return $this->data[$this->index]; }
	public function next() { ++$this->index; }
	public function key() { return $this->index; }
	public function valid() { return isset($this->data[$this->index]); }
	public function rewind() { $this->index = 0; }
	public function count() { return count($this->data); }
}
class ArrayOfFoo extends ArrayOf { protected $type = 'Foo'; }
class Foo { public function bar() { return __METHOD__; } }

$ctors = [];
$timers = [];
$fooze = [];
$arrays = [];
$callables = [];
$result = [];

$max = $argv[1] ? $argv[1] : 1000;

$start = microtime(true);
while (count($fooze) < $max) {
    $fooze[] = new Foo();
}
$ctors["native"] = microtime(true)-$start;

while (count($arrays) < $max) {
	$arrays[] = array();
	$callables[] = function(){};
}

$start = microtime(true);
$fooArray = new ArrayOfFoo; 
for ($i=0; count($fooArray) < $max; $i++) {
	$fooArray[$i] = new Foo;
}
$ctors["user"] = microtime(true)-$start;

$totalRuns = 500;
$timers = [
	"user_instanceof_foo" => $fooze,
	"user_filter_foo" => $fooze,
	"arrayof_foo" => $fooze,
	"user_arrays" => $arrays,
	"arrayof_arrays" => $arrays,
	"user_callables" => $callables,
	"arrayof_callables" => $callables,
	"user_interfaces" => $fooArray
];

for($i = 0; $i < $totalRuns; ++$i){
	foreach($timers as $callable => $arg){
		$alpha = microtime(true);
		$callable($arg);
		$result[$callable] += microtime(true) - $alpha;
	}
}

$result = array_map(function ($v) use($totalRuns) {
	return $v / $totalRuns;
}, $result);

asort($result, SORT_NUMERIC);
asort($ctors, SORT_NUMERIC);

printf("constructors [not very reliable timings]:\n");
foreach ($ctors as $name => $time) {
	printf("%-30s\t%.8f\n", $name, $time);
}
printf("\n");
printf("tests:\n");
foreach ($result as $name => $time) {
	printf("%-30s\t%.8f\n", 
		str_replace("_", " ", $name), $time);
}
?>

Note: each test function is run 500 times, averages provided

[joe@fiji php-src]$ sapi/cli/php -n -dmemory_limit=2G perf.php 10
constructors [not very reliable timings]:
native                          0.00001502
user                            0.00003695

tests:
arrayof arrays                  0.00000122
arrayof callables               0.00000202
user arrays                     0.00000209
arrayof foo                     0.00000223
user instanceof foo             0.00000227
user callables                  0.00000275
user filter foo                 0.00000468
user interfaces                 0.00000835

[joe@fiji php-src]$ sapi/cli/php -n -dmemory_limit=2G perf.php 100
constructors [not very reliable timings]:
native                          0.00004292
user                            0.00014400

tests:
arrayof arrays                  0.00000708
arrayof callables               0.00001340
user arrays                     0.00001344
arrayof foo                     0.00001379
user instanceof foo             0.00001475
user callables                  0.00002122
user filter foo                 0.00003038
user interfaces                 0.00006573

[joe@fiji php-src]$ sapi/cli/php -n -dmemory_limit=2G perf.php 1000
constructors [not very reliable timings]:
native                          0.00034094
user                            0.00122619

tests:
arrayof arrays                  0.00007181
arrayof foo                     0.00013031
user arrays                     0.00013081
arrayof callables               0.00013340
user instanceof foo             0.00014088
user callables                  0.00020693
user filter foo                 0.00028824
user interfaces                 0.00066317

[joe@fiji php-src]$ sapi/cli/php -n -dmemory_limit=2G perf.php 10000
constructors [not very reliable timings]:
native                          0.00346088
user                            0.01473308

tests:
arrayof arrays                  0.00235372
arrayof foo                     0.00243142
user arrays                     0.00320780
arrayof callables               0.00378044
user callables                  0.00433458
user filter foo                 0.00465060
user instanceof foo             0.00549773
user interfaces                 0.00862223

[joe@fiji php-src]$ sapi/cli/php -n -dmemory_limit=2G perf.php 100000
constructors [not very reliable timings]:
native                          0.03715611
user                            0.18054104

tests:
arrayof foo                     0.03434128
user arrays                     0.03928255
arrayof arrays                  0.04027043
arrayof callables               0.05607406
user filter foo                 0.05802278
user callables                  0.05824624
user instanceof foo             0.06152818
user interfaces                 0.13526678
@danbadds38
Copy link

This needs to be implemented! Currently Using __call() magic methods to use reflections to check doc types & use regex to get type definitions in the @method annotation for arguments to manually check for the values passed by a given array. would be nice to have this implemented in a supported language version!.

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