3v4l.org

run code in 500+ PHP versions simultaneously
<?php declare(strict_types=1); /* |-------------------------------------------------------------------------- | Benchmark: | | 1. Dynamic call: | $instance->$method(...$args) | | 2. Reflection: | ReflectionMethod::invoke() | | 3. Cached first-class callable: | self::method(...) | | 4. Cached closure + Closure::call() | | This benchmark is specifically useful for: | - AOP | - proxy dispatch | - interceptor systems | - generated wrappers | |-------------------------------------------------------------------------- */ final class Bench { private int $value = 42; public function targetMethod(int $a, int $b): int { return $this->value + $a + $b; } public function run(int $iterations = 5_000_000): void { $method = 'targetMethod'; $args = [10, 20]; echo "Iterations: {$iterations}\n"; echo "Object ID : " . spl_object_id($this) . "\n"; echo str_repeat('-', 70) . "\n"; /* * Warmup (important for fair benchmark) */ for ($i = 0; $i < 10000; $i++) { $this->targetMethod(1, 2); } /* * Reflection prepared once */ $reflection = new ReflectionMethod(self::class, 'targetMethod'); /* * First-class callable prepared once * * You can test BOTH variants: * * Variant A: * $cachedClosure = $this->targetMethod(...); * * Variant B: * $cachedClosure = self::targetMethod(...); * * (both valid inside object context) */ $cachedClosure = self::targetMethod(...); /* * Another object for Closure::call() */ $another = new self(); /* * Prevent optimizer from eliminating calls */ $checksum = 0; /* |-------------------------------------------------------------------------- | 1. Dynamic method call |-------------------------------------------------------------------------- */ $start = hrtime(true); for ($i = 0; $i < $iterations; $i++) { $checksum += $this->$method(...$args); } $dynamicTime = hrtime(true) - $start; /* |-------------------------------------------------------------------------- | 2. ReflectionMethod::invoke() |-------------------------------------------------------------------------- */ $start = hrtime(true); for ($i = 0; $i < $iterations; $i++) { $checksum += $reflection->invoke($this, ...$args); } $reflectionTime = hrtime(true) - $start; /* |-------------------------------------------------------------------------- | 3. Cached closure direct call |-------------------------------------------------------------------------- */ $start = hrtime(true); for ($i = 0; $i < $iterations; $i++) { $checksum += $cachedClosure(...$args); } $closureTime = hrtime(true) - $start; /* |-------------------------------------------------------------------------- | 4. Cached closure + Closure::call() |-------------------------------------------------------------------------- */ $start = hrtime(true); for ($i = 0; $i < $iterations; $i++) { $checksum += $cachedClosure->call($another, ...$args); } $callTime = hrtime(true) - $start; echo "Checksum (ignore): {$checksum}\n"; echo str_repeat('-', 70) . "\n"; $this->printResult('1. Dynamic $obj->$method()', $dynamicTime, $iterations); $this->printResult('2. ReflectionMethod::invoke()', $reflectionTime, $iterations); $this->printResult('3. Cached closure direct', $closureTime, $iterations); $this->printResult('4. Closure->call()', $callTime, $iterations); echo str_repeat('-', 70) . "\n"; echo "Lower is better\n"; } private function printResult( string $label, int $nanoseconds, int $iterations ): void { $ms = $nanoseconds / 1_000_000; $nsPerOp = $nanoseconds / $iterations; printf( "%-35s %12.3f ms (%8.2f ns/op)\n", $label, $ms, $nsPerOp ); } } (new Bench())->run();

Here you find the average performance (time & memory) of each version. A grayed out version indicates it didn't complete successfully (based on exit-code).

VersionSystem time (s)User time (s)Memory (MiB)
8.5.30.0101.11518.57
8.5.20.0201.12716.68
8.5.10.0301.08916.49
8.5.00.0081.12816.49
8.4.180.0061.08019.69
8.4.170.0151.08719.61
8.4.160.0151.06519.77
8.4.150.0341.11319.67
8.4.140.0171.09717.71
8.4.130.0241.07117.63
8.4.120.0251.14917.71
8.4.110.0231.07217.95
8.4.100.0171.05117.71
8.4.90.0381.10117.94
8.4.80.0341.10817.92
8.4.70.0221.06418.02
8.4.60.0211.05417.77
8.4.50.0161.05717.95
8.4.40.0361.07917.46
8.4.30.0221.09717.71
8.4.20.0281.08717.66
8.4.10.0301.07617.75
8.3.300.0221.07418.25
8.3.290.0221.12218.29
8.3.280.0201.08818.32
8.3.270.0321.06216.78
8.3.260.0231.08216.82
8.3.250.0201.05116.57
8.3.240.0281.06816.58
8.3.230.0251.05616.74
8.3.220.0231.06216.68
8.3.210.0201.04216.56
8.3.200.0151.08216.64
8.3.190.0281.14316.77
8.3.180.0131.05416.67
8.3.170.0261.06716.53
8.3.160.0201.05116.64
8.3.150.0291.02816.82
8.3.140.0191.04616.78
8.3.130.0271.02616.67
8.3.120.0291.03016.78
8.3.110.0161.02016.64
8.3.100.0291.04116.73
8.3.90.0191.06916.92
8.3.80.0181.02816.75
8.3.70.0251.05016.59
8.3.60.0201.03516.67
8.3.50.0261.02516.84
8.3.40.0191.08318.03
8.3.30.0141.17418.04
8.3.20.0171.08217.96
8.3.10.0141.12317.97
8.3.00.0161.09017.96

preferences:
52.35 ms | 734 KiB | 5 Q