From cf160789fdb88d9d3421b9aae5ba93b456fd2eb9 Mon Sep 17 00:00:00 2001 From: Arnaud Le Blanc Date: Tue, 4 Feb 2025 17:39:05 +0100 Subject: [PATCH] Optimize ReflectionProperty::getValue(), ::getRawValue(), ::isInitialized(), ::setValue(), ::setRawValue() - Pass a cache slot to the property handler - Inline the simple case of fetching a declared property - Use the new parameter parsing API This makes these methods 12% to 60% faster. Using the new parameter parsing API had the most impact, by far. --- ext/reflection/php_reflection.c | 118 +++++++++++++++++++++++--------- 1 file changed, 86 insertions(+), 32 deletions(-) diff --git a/ext/reflection/php_reflection.c b/ext/reflection/php_reflection.c index 724de0a49124..9d7442b13fb0 100644 --- a/ext/reflection/php_reflection.c +++ b/ext/reflection/php_reflection.c @@ -132,6 +132,7 @@ PHPAPI zend_class_entry *reflection_property_hook_type_ptr; typedef struct _property_reference { zend_property_info *prop; zend_string *unmangled_name; + void *cache_slot[3]; } property_reference; /* Struct for parameters */ @@ -1506,6 +1507,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_string *name, reference = (property_reference*) emalloc(sizeof(property_reference)); reference->prop = prop; reference->unmangled_name = zend_string_copy(name); + memset(reference->cache_slot, 0, sizeof(reference->cache_slot)); intern->ptr = reference; intern->ref_type = REF_TYPE_PROPERTY; intern->ce = ce; @@ -5643,6 +5645,7 @@ ZEND_METHOD(ReflectionProperty, __construct) reference = (property_reference*) emalloc(sizeof(property_reference)); reference->prop = dynam_prop ? NULL : property_info; reference->unmangled_name = zend_string_copy(name); + memset(reference->cache_slot, 0, sizeof(reference->cache_slot)); intern->ptr = reference; intern->ref_type = REF_TYPE_PROPERTY; intern->ce = ce; @@ -5794,9 +5797,10 @@ ZEND_METHOD(ReflectionProperty, getValue) zval *object = NULL; zval *member_p = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_EX(object, 1, 0) + ZEND_PARSE_PARAMETERS_END(); GET_REFLECTION_OBJECT_PTR(ref); @@ -5813,13 +5817,29 @@ ZEND_METHOD(ReflectionProperty, getValue) RETURN_THROWS(); } - /* TODO: Should this always use intern->ce? */ - if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) { - _DO_THROW("Given object is not an instance of the class this property was declared in"); - RETURN_THROWS(); + if (ref->cache_slot[0] == Z_OBJCE_P(object)) { + uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1]; + + if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { + zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset); + if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) { + RETURN_COPY_DEREF(retval); + } + } + } else { + /* TODO: Should this always use intern->ce? */ + if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) { + _DO_THROW("Given object is not an instance of the class this property was declared in"); + RETURN_THROWS(); + } } - member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv); + zend_class_entry *old_scope = EG(fake_scope); + EG(fake_scope) = intern->ce; + member_p = Z_OBJ_P(object)->handlers->read_property(Z_OBJ_P(object), + ref->unmangled_name, BP_VAR_R, ref->cache_slot, &rv); + EG(fake_scope) = old_scope; + if (member_p != &rv) { RETURN_COPY_DEREF(member_p); } else { @@ -5873,7 +5893,10 @@ ZEND_METHOD(ReflectionProperty, setValue) Z_PARAM_ZVAL(value) ZEND_PARSE_PARAMETERS_END(); - zend_update_property_ex(intern->ce, object, ref->unmangled_name, value); + zend_class_entry *old_scope = EG(fake_scope); + EG(fake_scope) = intern->ce; + object->handlers->write_property(object, ref->unmangled_name, value, ref->cache_slot); + EG(fake_scope) = old_scope; } } /* }}} */ @@ -5884,25 +5907,41 @@ ZEND_METHOD(ReflectionProperty, getRawValue) property_reference *ref; zval *object; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &object) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(1, 1) + Z_PARAM_OBJECT(object) + ZEND_PARSE_PARAMETERS_END(); GET_REFLECTION_OBJECT_PTR(ref); - if (prop_get_flags(ref) & ZEND_ACC_STATIC) { - _DO_THROW("May not use getRawValue on static properties"); - RETURN_THROWS(); - } + if (ref->cache_slot[0] == Z_OBJCE_P(object)) { + uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1]; - if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) { - _DO_THROW("Given object is not an instance of the class this property was declared in"); - RETURN_THROWS(); + if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { + zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset); + if (EXPECTED(Z_TYPE_INFO_P(retval) != IS_UNDEF)) { + RETURN_COPY_DEREF(retval); + } + } + } else { + if (prop_get_flags(ref) & ZEND_ACC_STATIC) { + _DO_THROW("May not use getRawValue on static properties"); + RETURN_THROWS(); + } + + if (!instanceof_function(Z_OBJCE_P(object), intern->ce)) { + _DO_THROW("Given object is not an instance of the class this property was declared in"); + RETURN_THROWS(); + } } if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_GET]) { zval rv; - zval *member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv); + zend_class_entry *old_scope = EG(fake_scope); + EG(fake_scope) = intern->ce; + zval *member_p = Z_OBJ_P(object)->handlers->read_property( + Z_OBJ_P(object), ref->unmangled_name, BP_VAR_R, + ref->cache_slot, &rv); + EG(fake_scope) = old_scope; if (member_p != &rv) { RETURN_COPY_DEREF(member_p); @@ -5921,7 +5960,10 @@ ZEND_METHOD(ReflectionProperty, getRawValue) static void reflection_property_set_raw_value(property_reference *ref, reflection_object *intern, zend_object *object, zval *value) { if (!ref->prop || !ref->prop->hooks || !ref->prop->hooks[ZEND_PROPERTY_HOOK_SET]) { - zend_update_property_ex(intern->ce, object, ref->unmangled_name, value); + zend_class_entry *old_scope = EG(fake_scope); + EG(fake_scope) = intern->ce; + object->handlers->write_property(object, ref->unmangled_name, value, ref->cache_slot); + EG(fake_scope) = old_scope; } else { zend_function *func = zend_get_property_hook_trampoline(ref->prop, ZEND_PROPERTY_HOOK_SET, ref->unmangled_name); zend_call_known_instance_method_with_1_params(func, object, NULL, value); @@ -5942,9 +5984,10 @@ ZEND_METHOD(ReflectionProperty, setRawValue) RETURN_THROWS(); } - if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(2, 2) { + Z_PARAM_OBJECT(object) + Z_PARAM_ZVAL(value) + } ZEND_PARSE_PARAMETERS_END(); reflection_property_set_raw_value(ref, intern, Z_OBJ_P(object), value); } @@ -6116,9 +6159,10 @@ ZEND_METHOD(ReflectionProperty, isInitialized) zval *object = NULL; zval *member_p = NULL; - if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) { - RETURN_THROWS(); - } + ZEND_PARSE_PARAMETERS_START(0, 1) + Z_PARAM_OPTIONAL + Z_PARAM_OBJECT_EX(object, 1, 0) + ZEND_PARSE_PARAMETERS_END(); GET_REFLECTION_OBJECT_PTR(ref); @@ -6137,15 +6181,25 @@ ZEND_METHOD(ReflectionProperty, isInitialized) RETURN_THROWS(); } - /* TODO: Should this always use intern->ce? */ - if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) { - _DO_THROW("Given object is not an instance of the class this property was declared in"); - RETURN_THROWS(); + if (ref->cache_slot[0] == Z_OBJCE_P(object)) { + uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1]; + + if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) { + zval *value = OBJ_PROP(Z_OBJ_P(object), prop_offset); + RETURN_BOOL(Z_TYPE_INFO_P(value) != IS_UNDEF); + } + } else { + /* TODO: Should this always use intern->ce? */ + if (!instanceof_function(Z_OBJCE_P(object), ref->prop ? ref->prop->ce : intern->ce)) { + _DO_THROW("Given object is not an instance of the class this property was declared in"); + RETURN_THROWS(); + } } old_scope = EG(fake_scope); EG(fake_scope) = intern->ce; - retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object), ref->unmangled_name, ZEND_PROPERTY_EXISTS, NULL); + retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object), + ref->unmangled_name, ZEND_PROPERTY_EXISTS, ref->cache_slot); EG(fake_scope) = old_scope; RETVAL_BOOL(retval);