Skip to content

Commit 6816285

Browse files
committed
Improve performance of instantiating exceptions/errors
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 ```
1 parent 00f0175 commit 6816285

File tree

2 files changed

+44
-26
lines changed

2 files changed

+44
-26
lines changed

UPGRADING

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,7 @@ PHP 8.5 UPGRADE NOTES
512512
- Core:
513513
. Remove OPcodes for identity comparisons against booleans, particularly
514514
for the match(true) pattern.
515+
. Creating exception objects is now much faster.
515516

516517
- ReflectionProperty:
517518
. Improved performance of the following methods: getValue(), getRawValue(),

Zend/zend_exceptions.c

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,33 @@ ZEND_API void zend_clear_exception(void) /* {{{ */
254254
}
255255
/* }}} */
256256

257+
/* Same as writing to OBJ_PROP_NUM() when there are no hooks,
258+
* but checks the offset is correct when Zend is built in debug mode.
259+
* This is faster than going through the regular property write routine when the offset is known at compile time. */
260+
static void zend_update_property_num_checked(zend_object *object, uint32_t prop_num, zend_string *member, zval *value)
261+
{
262+
if (UNEXPECTED(object->ce->num_hooked_props > 0)) {
263+
/* Property may have been overridden with a hook. */
264+
zend_update_property_ex(object->ce, object, member, value);
265+
zval_ptr_dtor(value);
266+
return;
267+
}
268+
#if ZEND_DEBUG
269+
zend_class_entry *old_scope = EG(fake_scope);
270+
EG(fake_scope) = i_get_exception_base(object);
271+
const zend_property_info *prop_info = zend_get_property_info(object->ce, member, true);
272+
ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_num);
273+
EG(fake_scope) = old_scope;
274+
#endif
275+
zval *zv = OBJ_PROP_NUM(object, prop_num);
276+
zval_ptr_safe_dtor(zv);
277+
ZVAL_COPY_VALUE(zv, value);
278+
}
279+
257280
static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */
258281
{
259282
zval tmp;
260283
zval trace;
261-
zend_class_entry *base_ce;
262284
zend_string *filename;
263285

264286
zend_object *object = zend_objects_new(class_type);
@@ -269,26 +291,23 @@ static zend_object *zend_default_exception_new(zend_class_entry *class_type) /*
269291
0,
270292
EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
271293
} else {
272-
array_init(&trace);
294+
ZVAL_EMPTY_ARRAY(&trace);
273295
}
274-
Z_SET_REFCOUNT(trace, 0);
275296

276-
base_ce = i_get_exception_base(object);
297+
zend_update_property_num_checked(object, 5, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
277298

278299
if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error)
279300
|| !(filename = zend_get_compiled_filename()))) {
280301
ZVAL_STRING(&tmp, zend_get_executed_filename());
281-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
282-
zval_ptr_dtor(&tmp);
302+
zend_update_property_num_checked(object, 3, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
283303
ZVAL_LONG(&tmp, zend_get_executed_lineno());
284-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
304+
zend_update_property_num_checked(object, 4, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
285305
} else {
286-
ZVAL_STR(&tmp, filename);
287-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
306+
ZVAL_STR_COPY(&tmp, filename);
307+
zend_update_property_num_checked(object, 3, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
288308
ZVAL_LONG(&tmp, zend_get_compiled_lineno());
289-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
309+
zend_update_property_num_checked(object, 4, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
290310
}
291-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
292311

293312
return object;
294313
}
@@ -308,27 +327,26 @@ ZEND_METHOD(Exception, __construct)
308327
zend_string *message = NULL;
309328
zend_long code = 0;
310329
zval tmp, *object, *previous = NULL;
311-
zend_class_entry *base_ce;
312330

313331
object = ZEND_THIS;
314-
base_ce = i_get_exception_base(Z_OBJ_P(object));
315332

316333
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
317334
RETURN_THROWS();
318335
}
319336

320337
if (message) {
321-
ZVAL_STR(&tmp, message);
322-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
338+
ZVAL_STR_COPY(&tmp, message);
339+
zend_update_property_num_checked(Z_OBJ_P(object), 0, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
323340
}
324341

325342
if (code) {
326343
ZVAL_LONG(&tmp, code);
327-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
344+
zend_update_property_num_checked(Z_OBJ_P(object), 2, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
328345
}
329346

330347
if (previous) {
331-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
348+
Z_ADDREF_P(previous);
349+
zend_update_property_num_checked(Z_OBJ_P(object), 6, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
332350
}
333351
}
334352
/* }}} */
@@ -368,34 +386,33 @@ ZEND_METHOD(ErrorException, __construct)
368386

369387
if (message) {
370388
ZVAL_STR_COPY(&tmp, message);
371-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
372-
zval_ptr_dtor(&tmp);
389+
zend_update_property_num_checked(Z_OBJ_P(object), 0, ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
373390
}
374391

375392
if (code) {
376393
ZVAL_LONG(&tmp, code);
377-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
394+
zend_update_property_num_checked(Z_OBJ_P(object), 2, ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
378395
}
379396

380397
if (previous) {
381-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
398+
Z_ADDREF_P(previous);
399+
zend_update_property_num_checked(Z_OBJ_P(object), 6, ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
382400
}
383401

384402
ZVAL_LONG(&tmp, severity);
385-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
403+
zend_update_property_num_checked(Z_OBJ_P(object), 7, ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
386404

387405
if (filename) {
388406
ZVAL_STR_COPY(&tmp, filename);
389-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
390-
zval_ptr_dtor(&tmp);
407+
zend_update_property_num_checked(Z_OBJ_P(object), 3, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
391408
}
392409

393410
if (!lineno_is_null) {
394411
ZVAL_LONG(&tmp, lineno);
395-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
412+
zend_update_property_num_checked(Z_OBJ_P(object), 4, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
396413
} else if (filename) {
397414
ZVAL_LONG(&tmp, 0);
398-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
415+
zend_update_property_num_checked(Z_OBJ_P(object), 4, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
399416
}
400417
}
401418
/* }}} */

0 commit comments

Comments
 (0)