From 367f303efafdd897aa80c8a9587865fa40f05b04 Mon Sep 17 00:00:00 2001 From: Niels Dossche <7771979+nielsdos@users.noreply.github.com> Date: Thu, 29 Aug 2024 20:13:29 +0200 Subject: [PATCH] Optimize DOM property access (#15626) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For the read and write implementation, store the handler pointer in the first cache slot. For the write implementation, use the second cache slot to store the property info. For a micro-benchmark that performs a write: ```php $dom = new DOMDocument; for ($i=0;$i<9999999;$i++) $dom->strictErrorChecking = false; ``` I obtain the following results on an i7-4790: ``` ./sapi/cli/php ./write.php ran 1.42 ± 0.08 times faster than ./sapi/cli/php_old ./write.php ``` For a micro-benchmark that performs a read: ```php $dom = new DOMDocument; for ($i=0;$i<9999999;$i++) $dom->strictErrorChecking; ``` I obtain the following results on the same machine: ``` ./sapi/cli/php ./read.php ran 1.29 ± 0.13 times faster than ./sapi/cli/php_old ./read.php ``` --- ext/dom/php_dom.c | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/ext/dom/php_dom.c b/ext/dom/php_dom.c index e69a2cd2056..42a6876b414 100644 --- a/ext/dom/php_dom.c +++ b/ext/dom/php_dom.c @@ -365,16 +365,31 @@ static zval *dom_get_property_ptr_ptr(zend_object *object, zend_string *name, in return NULL; } +static zend_always_inline const dom_prop_handler *dom_get_prop_handler(const dom_object *obj, zend_string *name, void **cache_slot) +{ + const dom_prop_handler *hnd = NULL; + + if (obj->prop_handler != NULL) { + if (cache_slot) { + hnd = *cache_slot; + } + if (!hnd) { + hnd = zend_hash_find_ptr(obj->prop_handler, name); + if (cache_slot) { + *cache_slot = (void *) hnd; + } + } + } + + return hnd; +} + /* {{{ dom_read_property */ zval *dom_read_property(zend_object *object, zend_string *name, int type, void **cache_slot, zval *rv) { dom_object *obj = php_dom_obj_from_obj(object); zval *retval; - dom_prop_handler *hnd = NULL; - - if (obj->prop_handler != NULL) { - hnd = zend_hash_find_ptr(obj->prop_handler, name); - } + const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot); if (hnd) { int ret = hnd->read_func(obj, rv); @@ -394,19 +409,25 @@ zval *dom_read_property(zend_object *object, zend_string *name, int type, void * zval *dom_write_property(zend_object *object, zend_string *name, zval *value, void **cache_slot) { dom_object *obj = php_dom_obj_from_obj(object); - dom_prop_handler *hnd = NULL; - - if (obj->prop_handler != NULL) { - hnd = zend_hash_find_ptr(obj->prop_handler, name); - } + const dom_prop_handler *hnd = dom_get_prop_handler(obj, name, cache_slot); if (hnd) { - if (!hnd->write_func) { + if (UNEXPECTED(!hnd->write_func)) { zend_readonly_property_modification_error_ex(ZSTR_VAL(object->ce->name), ZSTR_VAL(name)); return &EG(error_zval); } - zend_property_info *prop = zend_get_property_info(object->ce, name, /* silent */ true); + zend_property_info *prop = NULL; + if (cache_slot) { + prop = *(cache_slot + 1); + } + if (!prop) { + prop = zend_get_property_info(object->ce, name, /* silent */ true); + if (cache_slot) { + *(cache_slot + 1) = prop; + } + } + ZEND_ASSERT(prop && ZEND_TYPE_IS_SET(prop->type)); zval tmp; ZVAL_COPY(&tmp, value);