mirror of
https://github.com/php/php-src.git
synced 2026-03-24 00:02:20 +01:00
Improve performance of instantiating exceptions/errors (#18442)
The class structure is fixed, so it makes no sense to go through all the
logic of looking up property info etc if there are no hooks.
This patch introduces a local function `zend_update_property_num_checked()` to
help with that.
For this benchmark:
```php
for ($i = 0; $i < 1000000; $i++)
new Error;
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 141.6 ms ± 9.3 ms [User: 138.7 ms, System: 2.0 ms]
Range (min … max): 135.4 ms … 177.7 ms 20 runs
Benchmark 2: ../RELx64_old/sapi/cli/php x.php
Time (mean ± σ): 214.1 ms ± 7.0 ms [User: 207.6 ms, System: 5.0 ms]
Range (min … max): 206.6 ms … 230.9 ms 13 runs
Summary
./sapi/cli/php x.php ran
1.51 ± 0.11 times faster than ../RELx64_old/sapi/cli/php x.php
```
For this benchmark:
```php
for ($i = 0; $i < 1000000; $i++)
new Exception("message", 0, null);
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 184.3 ms ± 9.5 ms [User: 181.2 ms, System: 1.8 ms]
Range (min … max): 173.8 ms … 205.1 ms 15 runs
Benchmark 2: ../RELx64_old/sapi/cli/php x.php
Time (mean ± σ): 253.7 ms ± 7.0 ms [User: 247.6 ms, System: 4.6 ms]
Range (min … max): 245.7 ms … 263.7 ms 11 runs
Summary
./sapi/cli/php x.php ran
1.38 ± 0.08 times faster than ../RELx64_old/sapi/cli/php x.php
```
For this benchmark:
```php
for ($i = 0; $i < 1000000; $i++)
new ErrorException("message", 0, 0, "xyz", 0, null);
```
On an i7-4790:
```
Benchmark 1: ./sapi/cli/php x.php
Time (mean ± σ): 223.6 ms ± 7.7 ms [User: 220.1 ms, System: 2.4 ms]
Range (min … max): 216.9 ms … 242.5 ms 12 runs
Benchmark 2: ../RELx64_old/sapi/cli/php x.php
Time (mean ± σ): 343.5 ms ± 8.1 ms [User: 337.1 ms, System: 4.6 ms]
Range (min … max): 337.3 ms … 362.8 ms 10 runs
Summary
./sapi/cli/php x.php ran
1.54 ± 0.06 times faster than ../RELx64_old/sapi/cli/php x.php
```
This commit is contained in:
@@ -520,6 +520,7 @@ PHP 8.5 UPGRADE NOTES
|
||||
. Remove OPcodes for identity comparisons against booleans, particularly
|
||||
for the match(true) pattern.
|
||||
. Add OPcode specialization for `=== []` and `!== []` comparisons.
|
||||
. Creating exception objects is now much faster.
|
||||
|
||||
- ReflectionProperty:
|
||||
. Improved performance of the following methods: getValue(), getRawValue(),
|
||||
|
||||
@@ -30,6 +30,14 @@
|
||||
#include "zend_exceptions_arginfo.h"
|
||||
#include "zend_observer.h"
|
||||
|
||||
#define ZEND_EXCEPTION_MESSAGE_OFF 0
|
||||
#define ZEND_EXCEPTION_CODE_OFF 2
|
||||
#define ZEND_EXCEPTION_FILE_OFF 3
|
||||
#define ZEND_EXCEPTION_LINE_OFF 4
|
||||
#define ZEND_EXCEPTION_TRACE_OFF 5
|
||||
#define ZEND_EXCEPTION_PREVIOUS_OFF 6
|
||||
#define ZEND_EXCEPTION_SEVERITY_OFF 7
|
||||
|
||||
ZEND_API zend_class_entry *zend_ce_throwable;
|
||||
ZEND_API zend_class_entry *zend_ce_exception;
|
||||
ZEND_API zend_class_entry *zend_ce_error_exception;
|
||||
@@ -254,11 +262,33 @@ ZEND_API void zend_clear_exception(void) /* {{{ */
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/* Same as writing to OBJ_PROP_NUM() when there are no hooks,
|
||||
* but checks the offset is correct when Zend is built in debug mode.
|
||||
* This is faster than going through the regular property write routine when the offset is known at compile time. */
|
||||
static void zend_update_property_num_checked(zend_object *object, uint32_t prop_num, zend_string *member, zval *value)
|
||||
{
|
||||
if (UNEXPECTED(object->ce->num_hooked_props > 0)) {
|
||||
/* Property may have been overridden with a hook. */
|
||||
zend_update_property_ex(object->ce, object, member, value);
|
||||
zval_ptr_dtor(value);
|
||||
return;
|
||||
}
|
||||
#if ZEND_DEBUG
|
||||
zend_class_entry *old_scope = EG(fake_scope);
|
||||
EG(fake_scope) = i_get_exception_base(object);
|
||||
const zend_property_info *prop_info = zend_get_property_info(object->ce, member, true);
|
||||
ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_num);
|
||||
EG(fake_scope) = old_scope;
|
||||
#endif
|
||||
zval *zv = OBJ_PROP_NUM(object, prop_num);
|
||||
zval_ptr_safe_dtor(zv);
|
||||
ZVAL_COPY_VALUE(zv, value);
|
||||
}
|
||||
|
||||
static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */
|
||||
{
|
||||
zval tmp;
|
||||
zval trace;
|
||||
zend_class_entry *base_ce;
|
||||
zend_string *filename;
|
||||
|
||||
zend_object *object = zend_objects_new(class_type);
|
||||
@@ -269,26 +299,23 @@ static zend_object *zend_default_exception_new(zend_class_entry *class_type) /*
|
||||
0,
|
||||
EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
|
||||
} else {
|
||||
array_init(&trace);
|
||||
ZVAL_EMPTY_ARRAY(&trace);
|
||||
}
|
||||
Z_SET_REFCOUNT(trace, 0);
|
||||
|
||||
base_ce = i_get_exception_base(object);
|
||||
zend_update_property_num_checked(object, ZEND_EXCEPTION_TRACE_OFF, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
|
||||
|
||||
if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error)
|
||||
|| !(filename = zend_get_compiled_filename()))) {
|
||||
ZVAL_STRING(&tmp, zend_get_executed_filename());
|
||||
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
zval_ptr_dtor(&tmp);
|
||||
zend_update_property_num_checked(object, ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
ZVAL_LONG(&tmp, zend_get_executed_lineno());
|
||||
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
zend_update_property_num_checked(object, ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
} else {
|
||||
ZVAL_STR(&tmp, filename);
|
||||
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
ZVAL_STR_COPY(&tmp, filename);
|
||||
zend_update_property_num_checked(object, ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
ZVAL_LONG(&tmp, zend_get_compiled_lineno());
|
||||
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
zend_update_property_num_checked(object, ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
}
|
||||
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
|
||||
|
||||
return object;
|
||||
}
|
||||
@@ -308,27 +335,26 @@ ZEND_METHOD(Exception, __construct)
|
||||
zend_string *message = NULL;
|
||||
zend_long code = 0;
|
||||
zval tmp, *object, *previous = NULL;
|
||||
zend_class_entry *base_ce;
|
||||
|
||||
object = ZEND_THIS;
|
||||
base_ce = i_get_exception_base(Z_OBJ_P(object));
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
|
||||
RETURN_THROWS();
|
||||
}
|
||||
|
||||
if (message) {
|
||||
ZVAL_STR(&tmp, message);
|
||||
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
|
||||
ZVAL_STR_COPY(&tmp, message);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
|
||||
}
|
||||
|
||||
if (code) {
|
||||
ZVAL_LONG(&tmp, code);
|
||||
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
|
||||
}
|
||||
|
||||
if (previous) {
|
||||
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
|
||||
Z_ADDREF_P(previous);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
@@ -368,34 +394,33 @@ ZEND_METHOD(ErrorException, __construct)
|
||||
|
||||
if (message) {
|
||||
ZVAL_STR_COPY(&tmp, message);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
|
||||
zval_ptr_dtor(&tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_MESSAGE_OFF, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
|
||||
}
|
||||
|
||||
if (code) {
|
||||
ZVAL_LONG(&tmp, code);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_CODE_OFF, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
|
||||
}
|
||||
|
||||
if (previous) {
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
|
||||
Z_ADDREF_P(previous);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_PREVIOUS_OFF, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
|
||||
}
|
||||
|
||||
ZVAL_LONG(&tmp, severity);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_SEVERITY_OFF, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
|
||||
|
||||
if (filename) {
|
||||
ZVAL_STR_COPY(&tmp, filename);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
zval_ptr_dtor(&tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_FILE_OFF, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
|
||||
}
|
||||
|
||||
if (!lineno_is_null) {
|
||||
ZVAL_LONG(&tmp, lineno);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
} else if (filename) {
|
||||
ZVAL_LONG(&tmp, 0);
|
||||
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
zend_update_property_num_checked(Z_OBJ_P(object), ZEND_EXCEPTION_LINE_OFF, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
|
||||
}
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
Reference in New Issue
Block a user