mirror of
https://github.com/php/php-src.git
synced 2026-03-24 08:12:21 +01:00
During the Doctrine Core Team Meetup 2024 the Doctrine team investigated the
performance overhead of using `setRawValueWithoutLazyInitialization()` instead
of `setValue()` and came to the surprising conclusion that
`setRawValueWithoutLazyInitialization()` outperformed `setValue()`, despite
doing more work.
These two scripts are used as the benchmark:
<?php
class Foo
{
public $id;
public $foo1;
public $foo2;
public $foo3;
public $foo4;
}
$reflection = new ReflectionClass(Foo::class);
$properties = $reflection->getProperties();
for ($i = 0; $i < 1000000; $i++) {
$foo = new Foo();
foreach ($properties as $property) {
$property->setValue($foo, 1);
}
}
and
<?php
class Foo
{
public $id;
public $foo1;
public $foo2;
public $foo3;
public $foo4;
}
$reflection = new ReflectionClass(Foo::class);
$properties = $reflection->getProperties();
for ($i = 0; $i < 1000000; $i++) {
$foo = new Foo();
foreach ($properties as $property) {
$property->setRawValueWithoutLazyInitialization($foo, 1);
}
}
Benchmarking these with a current git master shows that `setValue()` is 50%
slower:
$ hyperfine -L script setValue,setRawValueWithoutLazyInitialization '/tmp/php-before /tmp/test/{script}.php'
Benchmark 1: /tmp/php-before /tmp/test/setValue.php
Time (mean ± σ): 216.0 ms ± 5.8 ms [User: 212.0 ms, System: 3.7 ms]
Range (min … max): 208.2 ms … 225.3 ms 13 runs
Benchmark 2: /tmp/php-before /tmp/test/setRawValueWithoutLazyInitialization.php
Time (mean ± σ): 145.6 ms ± 3.6 ms [User: 141.6 ms, System: 3.8 ms]
Range (min … max): 140.4 ms … 152.8 ms 20 runs
Summary
/tmp/php-before /tmp/test/setRawValueWithoutLazyInitialization.php ran
1.48 ± 0.05 times faster than /tmp/php-before /tmp/test/setValue.php
Looking into the “why” revealed that the `setValue()` script spent quite some
time in `zend_parse_parameters()`.
A 50% overhead can be significant, given that `setValue()` is commonly called
several thousand times in a single request when using Doctrine.
This commit changes the non-static property case of `setValue()` to make use of
the fast parameter parsing API and adjusts `getValue()` for consistency.
The resulting comparison shows that both `setValue()` and
`setRawValueWithoutLazyInitialization()` are now (almost) equal:
$ hyperfine -L script setValue,setRawValueWithoutLazyInitialization 'sapi/cli/php /tmp/test/{script}.php'
Benchmark 1: sapi/cli/php /tmp/test/setValue.php
Time (mean ± σ): 143.0 ms ± 6.4 ms [User: 139.4 ms, System: 3.4 ms]
Range (min … max): 134.8 ms … 157.7 ms 18 runs
Benchmark 2: sapi/cli/php /tmp/test/setRawValueWithoutLazyInitialization.php
Time (mean ± σ): 147.0 ms ± 5.5 ms [User: 143.0 ms, System: 3.6 ms]
Range (min … max): 139.9 ms … 159.8 ms 19 runs
Summary
sapi/cli/php /tmp/test/setValue.php ran
1.03 ± 0.06 times faster than sapi/cli/php /tmp/test/setRawValueWithoutLazyInitialization.php