Skip to content

Commit 21e46af

Browse files
committed
perf(zend): cache inheritance binding once per (ce, target) within a do_inheritance_ex walk
Signed-off-by: azjezz <azjezz@protonmail.com>
1 parent 452b797 commit 21e46af

4 files changed

Lines changed: 67 additions & 4 deletions

File tree

Zend/zend_compile.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,7 @@ void zend_init_compiler_data_structures(void) /* {{{ */
436436
CG(type_arg_depth) = 0;
437437
CG(type_arg_residual_token) = 0;
438438
CG(generic_scope) = NULL;
439+
CG(inheritance_binding_cache) = NULL;
439440
}
440441
/* }}} */
441442

Zend/zend_globals.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,8 @@ struct _zend_compiler_globals {
169169
int type_arg_residual_token;
170170

171171
struct _zend_generic_scope_entry *generic_scope;
172+
173+
struct _zend_inheritance_binding_cache *inheritance_binding_cache;
172174
#ifdef ZTS
173175
uint32_t copied_functions_count;
174176
#endif

Zend/zend_inheritance.c

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,50 @@ static bool zend_get_inheritance_binding_full(
954954
return false;
955955
}
956956

957+
static bool zend_get_inheritance_binding_full_cached(
958+
const zend_class_entry *ce,
959+
const zend_class_entry *target,
960+
zend_type *out_args,
961+
uint32_t out_capacity,
962+
uint32_t *out_arity)
963+
{
964+
zend_inheritance_binding_cache *cache = CG(inheritance_binding_cache);
965+
if (cache && cache->present && cache->ce == ce && cache->target == target) {
966+
if (!cache->valid) {
967+
return false;
968+
}
969+
970+
if (cache->arity > out_capacity) {
971+
return false;
972+
}
973+
974+
for (uint32_t i = 0; i < cache->arity; i++) {
975+
out_args[i] = cache->args[i];
976+
}
977+
978+
*out_arity = cache->arity;
979+
return true;
980+
}
981+
982+
bool result = zend_get_inheritance_binding_full(
983+
ce, target, out_args, out_capacity, out_arity);
984+
985+
if (cache) {
986+
cache->ce = ce;
987+
cache->target = target;
988+
cache->present = true;
989+
cache->valid = result;
990+
if (result && *out_arity <= ZEND_GENERIC_MAX_PARAMS) {
991+
cache->arity = *out_arity;
992+
for (uint32_t i = 0; i < *out_arity; i++) {
993+
cache->args[i] = out_args[i];
994+
}
995+
}
996+
}
997+
998+
return result;
999+
}
1000+
9571001
/* Fills out_args with target_ce's parameter defaults if every parameter
9581002
* has one. */
9591003
static bool zend_get_target_default_args(
@@ -997,7 +1041,7 @@ static zend_type zend_substitute_proto_type(
9971041

9981042
zend_type args[ZEND_GENERIC_MAX_PARAMS];
9991043
uint32_t arity;
1000-
if (!zend_get_inheritance_binding_full(ce, proto_scope, args, ZEND_GENERIC_MAX_PARAMS, &arity)
1044+
if (!zend_get_inheritance_binding_full_cached(ce, proto_scope, args, ZEND_GENERIC_MAX_PARAMS, &arity)
10011045
&& !zend_get_target_default_args(proto_scope, args, ZEND_GENERIC_MAX_PARAMS, &arity)) {
10021046
return fallback;
10031047
}
@@ -1547,7 +1591,7 @@ static zend_function *zend_maybe_substitute_inherited_method(
15471591
zend_class_entry *defining_ce = parent_fn->common.scope;
15481592
zend_type bound_args[ZEND_GENERIC_MAX_PARAMS];
15491593
uint32_t bound_arity = 0;
1550-
bool have = zend_get_inheritance_binding_full(
1594+
bool have = zend_get_inheritance_binding_full_cached(
15511595
ce, defining_ce, bound_args, ZEND_GENERIC_MAX_PARAMS, &bound_arity);
15521596

15531597
if (!have) {
@@ -1907,7 +1951,7 @@ static void do_inherit_property(zend_property_info *parent_info, zend_string *ke
19071951
const zend_type *pre_erasure = (const zend_type *) Z_PTR_P(zv);
19081952
zend_type bound_args[ZEND_GENERIC_MAX_PARAMS];
19091953
uint32_t bound_arity = 0;
1910-
bool have_args = zend_get_inheritance_binding_full(
1954+
bool have_args = zend_get_inheritance_binding_full_cached(
19111955
ce, parent_info->ce, bound_args, ZEND_GENERIC_MAX_PARAMS, &bound_arity);
19121956

19131957
if (!have_args) {
@@ -2237,6 +2281,11 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
22372281
zend_property_info *property_info;
22382282
zend_string *key;
22392283

2284+
zend_inheritance_binding_cache binding_cache;
2285+
binding_cache.present = false;
2286+
zend_inheritance_binding_cache *prev_binding_cache = CG(inheritance_binding_cache);
2287+
CG(inheritance_binding_cache) = &binding_cache;
2288+
22402289
if (parent_ce && !(ce->ce_flags & ZEND_ACC_INTERFACE)) {
22412290
const zend_type *extends_args = NULL;
22422291
uint32_t arity = 0;
@@ -2460,6 +2509,8 @@ ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *par
24602509
}
24612510
}
24622511
ce->ce_flags |= parent_ce->ce_flags & (ZEND_HAS_STATIC_IN_METHODS | ZEND_ACC_HAS_TYPE_HINTS | ZEND_ACC_HAS_READONLY_PROPS | ZEND_ACC_USE_GUARDS | ZEND_ACC_NOT_SERIALIZABLE | ZEND_ACC_ALLOW_DYNAMIC_PROPERTIES);
2512+
2513+
CG(inheritance_binding_cache) = prev_binding_cache;
24632514
}
24642515
/* }}} */
24652516

@@ -4339,7 +4390,7 @@ static void zend_diamond_collect_via_provider(
43394390

43404391
zend_type via[ZEND_GENERIC_MAX_PARAMS];
43414392
uint32_t via_arity;
4342-
if (!zend_get_inheritance_binding_full(provider, target, via, ZEND_GENERIC_MAX_PARAMS, &via_arity)) continue;
4393+
if (!zend_get_inheritance_binding_full_cached(provider, target, via, ZEND_GENERIC_MAX_PARAMS, &via_arity)) continue;
43434394

43444395
if (ce_to_provider) {
43454396
for (uint32_t j = 0; j < via_arity; j++) {

Zend/zend_inheritance.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ typedef enum {
3131
INHERITANCE_SUCCESS = 2,
3232
} zend_inheritance_status;
3333

34+
typedef struct _zend_inheritance_binding_cache {
35+
const zend_class_entry *ce;
36+
const zend_class_entry *target;
37+
uint32_t arity;
38+
bool present;
39+
bool valid;
40+
zend_type args[ZEND_GENERIC_MAX_PARAMS];
41+
} zend_inheritance_binding_cache;
42+
3443
ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface);
3544
ZEND_API void zend_do_inheritance_ex(zend_class_entry *ce, zend_class_entry *parent_ce, bool checked);
3645
ZEND_API zend_inheritance_status zend_check_generic_arg_satisfies_bound(

0 commit comments

Comments
 (0)