Skip to content

Commit 8af9042

Browse files
authored
Use new ZPP and inline caches in ReflectionProperty::getValue() and variants (#17698)
Apply the following changes to 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 results in run time decrease of 12% to 59% in [micro benchmarks](https://gist.github.com/arnaud-lb/2de08142dcd0c7b49caed21398f44656), when executed with `hyperfine -L version base,opt "/tmp/{version}/sapi/cli/php -d opcache.enable_cli=1 $script"`: ``` get-raw-value.php: -58% get-value-dyn.php: -50% get-value-hook.php: -47% get-value.php: -59% is-initialized.php: -59% set-value.php: -11% ``` And a 1.8% decrease in this Doctrine benchmark: arnaud-lb/symfony-demo@5058252...reflection-property-get-value-opt
1 parent 395c3fb commit 8af9042

File tree

1 file changed

+76
-21
lines changed

1 file changed

+76
-21
lines changed

ext/reflection/php_reflection.c

+76-21
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ PHPAPI zend_class_entry *reflection_property_hook_type_ptr;
132132
typedef struct _property_reference {
133133
zend_property_info *prop;
134134
zend_string *unmangled_name;
135+
void *cache_slot[3];
135136
} property_reference;
136137

137138
/* Struct for parameters */
@@ -1506,6 +1507,7 @@ static void reflection_property_factory(zend_class_entry *ce, zend_string *name,
15061507
reference = (property_reference*) emalloc(sizeof(property_reference));
15071508
reference->prop = prop;
15081509
reference->unmangled_name = zend_string_copy(name);
1510+
memset(reference->cache_slot, 0, sizeof(reference->cache_slot));
15091511
intern->ptr = reference;
15101512
intern->ref_type = REF_TYPE_PROPERTY;
15111513
intern->ce = ce;
@@ -5638,6 +5640,7 @@ ZEND_METHOD(ReflectionProperty, __construct)
56385640
reference = (property_reference*) emalloc(sizeof(property_reference));
56395641
reference->prop = dynam_prop ? NULL : property_info;
56405642
reference->unmangled_name = zend_string_copy(name);
5643+
memset(reference->cache_slot, 0, sizeof(reference->cache_slot));
56415644
intern->ptr = reference;
56425645
intern->ref_type = REF_TYPE_PROPERTY;
56435646
intern->ce = ce;
@@ -5789,9 +5792,10 @@ ZEND_METHOD(ReflectionProperty, getValue)
57895792
zval *object = NULL;
57905793
zval *member_p = NULL;
57915794

5792-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) {
5793-
RETURN_THROWS();
5794-
}
5795+
ZEND_PARSE_PARAMETERS_START(0, 1)
5796+
Z_PARAM_OPTIONAL
5797+
Z_PARAM_OBJECT_EX(object, 1, 0)
5798+
ZEND_PARSE_PARAMETERS_END();
57955799

57965800
GET_REFLECTION_OBJECT_PTR(ref);
57975801

@@ -5814,7 +5818,23 @@ ZEND_METHOD(ReflectionProperty, getValue)
58145818
RETURN_THROWS();
58155819
}
58165820

5817-
member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv);
5821+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
5822+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
5823+
5824+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
5825+
zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset);
5826+
if (EXPECTED(!Z_ISUNDEF_P(retval))) {
5827+
RETURN_COPY_DEREF(retval);
5828+
}
5829+
}
5830+
}
5831+
5832+
zend_class_entry *old_scope = EG(fake_scope);
5833+
EG(fake_scope) = intern->ce;
5834+
member_p = Z_OBJ_P(object)->handlers->read_property(Z_OBJ_P(object),
5835+
ref->unmangled_name, BP_VAR_R, ref->cache_slot, &rv);
5836+
EG(fake_scope) = old_scope;
5837+
58185838
if (member_p != &rv) {
58195839
RETURN_COPY_DEREF(member_p);
58205840
} else {
@@ -5868,7 +5888,10 @@ ZEND_METHOD(ReflectionProperty, setValue)
58685888
Z_PARAM_ZVAL(value)
58695889
ZEND_PARSE_PARAMETERS_END();
58705890

5871-
zend_update_property_ex(intern->ce, object, ref->unmangled_name, value);
5891+
zend_class_entry *old_scope = EG(fake_scope);
5892+
EG(fake_scope) = intern->ce;
5893+
object->handlers->write_property(object, ref->unmangled_name, value, ref->cache_slot);
5894+
EG(fake_scope) = old_scope;
58725895
}
58735896
}
58745897
/* }}} */
@@ -5892,9 +5915,9 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
58925915
property_reference *ref;
58935916
zval *object;
58945917

5895-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "o", &object) == FAILURE) {
5896-
RETURN_THROWS();
5897-
}
5918+
ZEND_PARSE_PARAMETERS_START(1, 1)
5919+
Z_PARAM_OBJECT(object)
5920+
ZEND_PARSE_PARAMETERS_END();
58985921

58995922
GET_REFLECTION_OBJECT_PTR(ref);
59005923

@@ -5903,6 +5926,17 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
59035926
RETURN_THROWS();
59045927
}
59055928

5929+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
5930+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
5931+
5932+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
5933+
zval *retval = OBJ_PROP(Z_OBJ_P(object), prop_offset);
5934+
if (EXPECTED(!Z_ISUNDEF_P(retval))) {
5935+
RETURN_COPY_DEREF(retval);
5936+
}
5937+
}
5938+
}
5939+
59065940
zend_property_info *prop = reflection_property_get_effective_prop(ref,
59075941
intern->ce, Z_OBJ_P(object));
59085942

@@ -5913,7 +5947,12 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
59135947

59145948
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_GET]) {
59155949
zval rv;
5916-
zval *member_p = zend_read_property_ex(intern->ce, Z_OBJ_P(object), ref->unmangled_name, 0, &rv);
5950+
zend_class_entry *old_scope = EG(fake_scope);
5951+
EG(fake_scope) = intern->ce;
5952+
zval *member_p = Z_OBJ_P(object)->handlers->read_property(
5953+
Z_OBJ_P(object), ref->unmangled_name, BP_VAR_R,
5954+
ref->cache_slot, &rv);
5955+
EG(fake_scope) = old_scope;
59175956

59185957
if (member_p != &rv) {
59195958
RETURN_COPY_DEREF(member_p);
@@ -5930,11 +5969,14 @@ ZEND_METHOD(ReflectionProperty, getRawValue)
59305969
}
59315970

59325971
static void reflection_property_set_raw_value(zend_property_info *prop,
5933-
zend_string *unmangled_name, reflection_object *intern,
5972+
zend_string *unmangled_name, void *cache_slot[3], reflection_object *intern,
59345973
zend_object *object, zval *value)
59355974
{
59365975
if (!prop || !prop->hooks || !prop->hooks[ZEND_PROPERTY_HOOK_SET]) {
5937-
zend_update_property_ex(intern->ce, object, unmangled_name, value);
5976+
zend_class_entry *old_scope = EG(fake_scope);
5977+
EG(fake_scope) = intern->ce;
5978+
object->handlers->write_property(object, unmangled_name, value, cache_slot);
5979+
EG(fake_scope) = old_scope;
59385980
} else {
59395981
zend_function *func = zend_get_property_hook_trampoline(prop, ZEND_PROPERTY_HOOK_SET, unmangled_name);
59405982
zend_call_known_instance_method_with_1_params(func, object, NULL, value);
@@ -5950,9 +5992,10 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
59505992

59515993
GET_REFLECTION_OBJECT_PTR(ref);
59525994

5953-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "oz", &object, &value) == FAILURE) {
5954-
RETURN_THROWS();
5955-
}
5995+
ZEND_PARSE_PARAMETERS_START(2, 2) {
5996+
Z_PARAM_OBJECT(object)
5997+
Z_PARAM_ZVAL(value)
5998+
} ZEND_PARSE_PARAMETERS_END();
59565999

59576000
zend_property_info *prop = reflection_property_get_effective_prop(ref,
59586001
intern->ce, Z_OBJ_P(object));
@@ -5962,7 +6005,8 @@ ZEND_METHOD(ReflectionProperty, setRawValue)
59626005
RETURN_THROWS();
59636006
}
59646007

5965-
reflection_property_set_raw_value(prop, ref->unmangled_name, intern, Z_OBJ_P(object), value);
6008+
reflection_property_set_raw_value(prop, ref->unmangled_name,
6009+
ref->cache_slot, intern, Z_OBJ_P(object), value);
59666010
}
59676011

59686012
static zend_result reflection_property_check_lazy_compatible(
@@ -6041,8 +6085,8 @@ ZEND_METHOD(ReflectionProperty, setRawValueWithoutLazyInitialization)
60416085
/* Do not trigger initialization */
60426086
Z_PROP_FLAG_P(var_ptr) &= ~IS_PROP_LAZY;
60436087

6044-
reflection_property_set_raw_value(prop, ref->unmangled_name, intern, object,
6045-
value);
6088+
reflection_property_set_raw_value(prop, ref->unmangled_name,
6089+
ref->cache_slot, intern, object, value);
60466090

60476091
/* Mark property as lazy again if an exception prevented update */
60486092
if (EG(exception) && prop_was_lazy && Z_TYPE_P(var_ptr) == IS_UNDEF
@@ -6138,9 +6182,10 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
61386182
zval *object = NULL;
61396183
zval *member_p = NULL;
61406184

6141-
if (zend_parse_parameters(ZEND_NUM_ARGS(), "|o!", &object) == FAILURE) {
6142-
RETURN_THROWS();
6143-
}
6185+
ZEND_PARSE_PARAMETERS_START(0, 1)
6186+
Z_PARAM_OPTIONAL
6187+
Z_PARAM_OBJECT_EX(object, 1, 0)
6188+
ZEND_PARSE_PARAMETERS_END();
61446189

61456190
GET_REFLECTION_OBJECT_PTR(ref);
61466191

@@ -6165,9 +6210,19 @@ ZEND_METHOD(ReflectionProperty, isInitialized)
61656210
RETURN_THROWS();
61666211
}
61676212

6213+
if (ref->cache_slot[0] == Z_OBJCE_P(object)) {
6214+
uintptr_t prop_offset = (uintptr_t) ref->cache_slot[1];
6215+
6216+
if (EXPECTED(IS_VALID_PROPERTY_OFFSET(prop_offset))) {
6217+
zval *value = OBJ_PROP(Z_OBJ_P(object), prop_offset);
6218+
RETURN_BOOL(!Z_ISUNDEF_P(value));
6219+
}
6220+
}
6221+
61686222
old_scope = EG(fake_scope);
61696223
EG(fake_scope) = intern->ce;
6170-
retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object), ref->unmangled_name, ZEND_PROPERTY_EXISTS, NULL);
6224+
retval = Z_OBJ_HT_P(object)->has_property(Z_OBJ_P(object),
6225+
ref->unmangled_name, ZEND_PROPERTY_EXISTS, ref->cache_slot);
61716226
EG(fake_scope) = old_scope;
61726227

61736228
RETVAL_BOOL(retval);

0 commit comments

Comments
 (0)