[wp-trac] [WordPress Trac] #56541: Performance: Don't execute jumps in arrays
WordPress Trac
noreply at wordpress.org
Fri Sep 9 15:13:12 UTC 2022
#56541: Performance: Don't execute jumps in arrays
--------------------------+-----------------------------
Reporter: Cybr | Owner: (none)
Type: defect (bug) | Status: new
Priority: normal | Milestone: Awaiting Review
Component: General | Version: trunk
Severity: normal | Keywords:
Focuses: performance |
--------------------------+-----------------------------
To spare CPU time stalling on branch predictors
([https://stackoverflow.com/a/9820411 briefly explained]), it's better to
execute as few jumps in the code, especially when we can easily modify
them. The simplest form of implementing a jump in PHP is via an
`if`-statement. Executing those inside loops will increase CPU time
quickly.
For example, and probably the most significant, in `wpdb::get_results()`,
the following code block could be converted to the next code block.
Current:
{{{#!php
<?php
foreach ( (array) $this->last_result as $row ) {
if ( ARRAY_N === $output ) {
// ...integer-keyed row arrays.
$new_array[] = array_values( get_object_vars( $row ) );
} else {
// ...column name-keyed row arrays.
$new_array[] = get_object_vars( $row );
}
}
}}}
My proposal:
{{{#!php
<?php
if ( ARRAY_N === $output ) {
foreach ( (array) $this->last_result as $row ) {
// ...integer-keyed row arrays.
$new_array[] = array_values( get_object_vars( $row ) );
}
} else {
foreach ( (array) $this->last_result as $row ) {
// ...column name-keyed row arrays.
$new_array[] = get_object_vars( $row );
}
}
}}}
Yes, that is more code outputting the same data, but it executes faster.
To get an indication of the time saved when querying 2000 items, see
https://3v4l.org/KvpIR#tabs.
Across the board, there's about a 10% performance improvement for the `if`
branch, and 20% for the `else` branch. When we loop over 80,000 results,
which I don't think is out of the ordinary, the results become
significant: https://3v4l.org/6OOHn#tabs. This is by optimizing just one
`foreach` loop. Applying these savings everywhere could yield not only
measurable but noticeable improvements.
I found another example at `WP_Upgrader_Skin::error()`, withal which keeps
calling the same `WP_Error::get_error_data()` method up to three times per
loop:
{{{#!php
<?php
foreach ( $errors->get_error_messages() as $message ) {
if ( $errors->get_error_data() && is_string(
$errors->get_error_data() ) ) {
$this->feedback( $message . ' ' . esc_html( strip_tags(
$errors->get_error_data() ) ) );
} else {
$this->feedback( $message );
}
}
}}}
Therefor my proposal, to which I added a `trim()` call to spare us an if-
statement all the same:
{{{#!php
<?php
$error_data = $errors->get_error_data();
$error_data = is_string( $error_data ) ? esc_html( strip_tags( $error_data
) ) : '';
foreach ( $errors->get_error_messages() as $message ) {
$this->feedback( trim( "$message $error_data" ) );
}
}}}
The principle is this: Do not execute work inside a loop that should be
done outside of it.
--
Ticket URL: <https://core.trac.wordpress.org/ticket/56541>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform
More information about the wp-trac
mailing list