Skip to content

Commit 0be5bb0

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. This patch introduces a local function `zend_obj_prop_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 c919ab4 commit 0be5bb0

File tree

2 files changed

+57
-42
lines changed

2 files changed

+57
-42
lines changed

UPGRADING

+3
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,9 @@ PHP 8.5 UPGRADE NOTES
469469
14. Performance Improvements
470470
========================================
471471

472+
- Core:
473+
. Creating exceptions objects is now up to 50% faster.
474+
472475
- ReflectionProperty:
473476
. Improved performance of the following methods: getValue(), getRawValue(),
474477
isInitialized(), setValue(), setRawValue().

Zend/zend_exceptions.c

+54-42
Original file line numberDiff line numberDiff line change
@@ -254,41 +254,45 @@ ZEND_API void zend_clear_exception(void) /* {{{ */
254254
}
255255
/* }}} */
256256

257+
/* Same as OBJ_PROP_NUM(), but checks the offset is correct when Zend is built in debug mode. */
258+
static zend_always_inline zval *zend_obj_prop_num_checked(zend_object *object, uint32_t prop_num, zend_string *str)
259+
{
260+
#if ZEND_DEBUG
261+
zend_class_entry *old_scope = EG(fake_scope);
262+
EG(fake_scope) = i_get_exception_base(object);
263+
const zend_property_info *prop_info = zend_get_property_info(object->ce, str, true);
264+
ZEND_ASSERT(OBJ_PROP_TO_NUM(prop_info->offset) == prop_num);
265+
EG(fake_scope) = old_scope;
266+
#else
267+
ZEND_IGNORE_VALUE(str);
268+
#endif
269+
return OBJ_PROP_NUM(object, prop_num);
270+
}
271+
257272
static zend_object *zend_default_exception_new(zend_class_entry *class_type) /* {{{ */
258273
{
259-
zval tmp;
260-
zval trace;
261-
zend_class_entry *base_ce;
262274
zend_string *filename;
263275

264276
zend_object *object = zend_objects_new(class_type);
265277
object_properties_init(object, class_type);
278+
zval *trace = zend_obj_prop_num_checked(object, 5, ZSTR_KNOWN(ZEND_STR_TRACE));
266279

267280
if (EG(current_execute_data)) {
268-
zend_fetch_debug_backtrace(&trace,
281+
zend_fetch_debug_backtrace(trace,
269282
0,
270283
EG(exception_ignore_args) ? DEBUG_BACKTRACE_IGNORE_ARGS : 0, 0);
271284
} else {
272-
array_init(&trace);
285+
ZVAL_EMPTY_ARRAY(trace);
273286
}
274-
Z_SET_REFCOUNT(trace, 0);
275-
276-
base_ce = i_get_exception_base(object);
277287

278288
if (EXPECTED((class_type != zend_ce_parse_error && class_type != zend_ce_compile_error)
279289
|| !(filename = zend_get_compiled_filename()))) {
280-
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);
283-
ZVAL_LONG(&tmp, zend_get_executed_lineno());
284-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
290+
ZVAL_STRING(zend_obj_prop_num_checked(object, 3, ZSTR_KNOWN(ZEND_STR_FILE)), zend_get_executed_filename());
291+
ZVAL_LONG(zend_obj_prop_num_checked(object, 4, ZSTR_KNOWN(ZEND_STR_LINE)), zend_get_executed_lineno());
285292
} else {
286-
ZVAL_STR(&tmp, filename);
287-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_FILE), &tmp);
288-
ZVAL_LONG(&tmp, zend_get_compiled_lineno());
289-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
293+
ZVAL_STR_COPY(zend_obj_prop_num_checked(object, 3, ZSTR_KNOWN(ZEND_STR_FILE)), filename);
294+
ZVAL_LONG(zend_obj_prop_num_checked(object, 4, ZSTR_KNOWN(ZEND_STR_LINE)), zend_get_compiled_lineno());
290295
}
291-
zend_update_property_ex(base_ce, object, ZSTR_KNOWN(ZEND_STR_TRACE), &trace);
292296

293297
return object;
294298
}
@@ -307,28 +311,30 @@ ZEND_METHOD(Exception, __construct)
307311
{
308312
zend_string *message = NULL;
309313
zend_long code = 0;
310-
zval tmp, *object, *previous = NULL;
311-
zend_class_entry *base_ce;
314+
zval *object, *previous = NULL;
312315

313316
object = ZEND_THIS;
314-
base_ce = i_get_exception_base(Z_OBJ_P(object));
315317

316318
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SlO!", &message, &code, &previous, zend_ce_throwable) == FAILURE) {
317319
RETURN_THROWS();
318320
}
319321

320322
if (message) {
321-
ZVAL_STR(&tmp, message);
322-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_MESSAGE), &tmp);
323+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 0, ZSTR_KNOWN(ZEND_STR_MESSAGE));
324+
zval_ptr_dtor(tmp);
325+
ZVAL_STR_COPY(tmp, message);
323326
}
324327

325328
if (code) {
326-
ZVAL_LONG(&tmp, code);
327-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
329+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 2, ZSTR_KNOWN(ZEND_STR_CODE));
330+
zval_ptr_dtor(tmp);
331+
ZVAL_LONG(tmp, code);
328332
}
329333

330334
if (previous) {
331-
zend_update_property_ex(base_ce, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
335+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 6, ZSTR_KNOWN(ZEND_STR_PREVIOUS));
336+
zval_ptr_dtor(tmp);
337+
ZVAL_COPY(tmp, previous);
332338
}
333339
}
334340
/* }}} */
@@ -358,7 +364,7 @@ ZEND_METHOD(ErrorException, __construct)
358364
zend_string *message = NULL, *filename = NULL;
359365
zend_long code = 0, severity = E_ERROR, lineno;
360366
bool lineno_is_null = 1;
361-
zval tmp, *object, *previous = NULL;
367+
zval *object, *previous = NULL;
362368

363369
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|SllS!l!O!", &message, &code, &severity, &filename, &lineno, &lineno_is_null, &previous, zend_ce_throwable) == FAILURE) {
364370
RETURN_THROWS();
@@ -367,35 +373,41 @@ ZEND_METHOD(ErrorException, __construct)
367373
object = ZEND_THIS;
368374

369375
if (message) {
370-
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);
376+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 0, ZSTR_KNOWN(ZEND_STR_MESSAGE));
377+
zval_ptr_dtor(tmp);
378+
ZVAL_STR_COPY(tmp, message);
373379
}
374380

375381
if (code) {
376-
ZVAL_LONG(&tmp, code);
377-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_CODE), &tmp);
382+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 2, ZSTR_KNOWN(ZEND_STR_CODE));
383+
zval_ptr_dtor(tmp);
384+
ZVAL_LONG(tmp, code);
378385
}
379386

380387
if (previous) {
381-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_PREVIOUS), previous);
388+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 6, ZSTR_KNOWN(ZEND_STR_PREVIOUS));
389+
zval_ptr_dtor(tmp);
390+
ZVAL_COPY(tmp, previous);
382391
}
383392

384-
ZVAL_LONG(&tmp, severity);
385-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_SEVERITY), &tmp);
393+
{
394+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 7, ZSTR_KNOWN(ZEND_STR_SEVERITY));
395+
zval_ptr_dtor(tmp);
396+
ZVAL_LONG(tmp, severity);
397+
}
386398

387399
if (filename) {
388-
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);
400+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 3, ZSTR_KNOWN(ZEND_STR_FILE));
401+
zval_ptr_dtor(tmp);
402+
ZVAL_STR_COPY(tmp, filename);
391403
}
392404

405+
zval *tmp = zend_obj_prop_num_checked(Z_OBJ_P(object), 4, ZSTR_KNOWN(ZEND_STR_LINE));
406+
zval_ptr_dtor(tmp);
393407
if (!lineno_is_null) {
394-
ZVAL_LONG(&tmp, lineno);
395-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
408+
ZVAL_LONG(tmp, lineno);
396409
} else if (filename) {
397-
ZVAL_LONG(&tmp, 0);
398-
zend_update_property_ex(zend_ce_exception, Z_OBJ_P(object), ZSTR_KNOWN(ZEND_STR_LINE), &tmp);
410+
ZVAL_LONG(tmp, 0);
399411
}
400412
}
401413
/* }}} */

0 commit comments

Comments
 (0)