From 4884fec7896cd3ecf5a1344ee953256b1146e7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 25 Aug 2025 09:27:37 +0200 Subject: [PATCH 1/2] zend_weakrefs: Remove layer of indirection in `zend_weakrefs_hash_clean()` Following php/php-src#16439. --- Zend/zend_weakrefs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index ae8879f360b80..6574a72c59350 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -190,7 +190,9 @@ ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) { ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { zend_ulong obj_key; ZEND_HASH_FOREACH_NUM_KEY(ht, obj_key) { - zend_weakrefs_hash_del(ht, zend_weakref_key_to_object(obj_key)); + /* Call zend_weakref_unregister to avoid an unneccessary + * zend_hash_index_find lookup in zend_weakrefs_hash_del. */ + zend_weakref_unregister(zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_BARE_HT), 1); } ZEND_HASH_FOREACH_END(); } From 08de1355107605343a51ce86a36714b4dd138bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim=20D=C3=BCsterhus?= Date: Mon, 25 Aug 2025 12:47:22 +0200 Subject: [PATCH 2/2] zend_weakrefs: Include `zend_weakmap_free_obj()` optimization in `zend_weakrefs_hash_clean()` --- Zend/zend_weakrefs.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Zend/zend_weakrefs.c b/Zend/zend_weakrefs.c index 6574a72c59350..0c729755ff445 100644 --- a/Zend/zend_weakrefs.c +++ b/Zend/zend_weakrefs.c @@ -138,8 +138,8 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea if (weakref_free) { zend_weakref_unref_single(ptr, tag, object); } else { - /* The optimization of skipping unref is only used in the destructor of WeakMap */ - ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP); + /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ + ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); } return; } @@ -163,8 +163,8 @@ static void zend_weakref_unregister(zend_object *object, void *payload, bool wea zend_weakref_unref_single( ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), object); } else { - /* The optimization of skipping unref is only used in the destructor of WeakMap */ - ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP); + /* The optimization of skipping unref is used for zend_weakrefs_hash_clean_ex() */ + ZEND_ASSERT(ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_MAP || ZEND_WEAKREF_GET_TAG(payload) == ZEND_WEAKREF_TAG_BARE_HT); } } @@ -187,13 +187,20 @@ ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) { return FAILURE; } -ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { +static void zend_weakrefs_hash_clean_ex(HashTable *ht, int type) { zend_ulong obj_key; - ZEND_HASH_FOREACH_NUM_KEY(ht, obj_key) { - /* Call zend_weakref_unregister to avoid an unneccessary - * zend_hash_index_find lookup in zend_weakrefs_hash_del. */ - zend_weakref_unregister(zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_BARE_HT), 1); + ZEND_HASH_MAP_FOREACH_NUM_KEY(ht, obj_key) { + /* Optimization: Don't call zend_weakref_unref_single to free individual entries from ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions). + * Let freeing the corresponding values for WeakMap entries be done in zend_hash_clean, freeing objects sequentially. + * The performance difference is notable for larger WeakMaps with worse cache locality. */ + zend_weakref_unregister( + zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(ht, type), 0); } ZEND_HASH_FOREACH_END(); + zend_hash_clean(ht); +} + +ZEND_API void zend_weakrefs_hash_clean(HashTable *ht) { + zend_weakrefs_hash_clean_ex(ht, ZEND_WEAKREF_TAG_BARE_HT); } void zend_weakrefs_init(void) { @@ -342,14 +349,7 @@ static zend_object *zend_weakmap_create_object(zend_class_entry *ce) static void zend_weakmap_free_obj(zend_object *object) { zend_weakmap *wm = zend_weakmap_from(object); - zend_ulong obj_key; - ZEND_HASH_MAP_FOREACH_NUM_KEY(&wm->ht, obj_key) { - /* Optimization: Don't call zend_weakref_unref_single to free individual entries from wm->ht when unregistering (which would do a hash table lookup, call zend_hash_index_del, and skip over any bucket collisions). - * Let freeing the corresponding values for WeakMap entries be done in zend_hash_destroy, freeing objects sequentially. - * The performance difference is notable for larger WeakMaps with worse cache locality. */ - zend_weakref_unregister( - zend_weakref_key_to_object(obj_key), ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 0); - } ZEND_HASH_FOREACH_END(); + zend_weakrefs_hash_clean_ex(&wm->ht, ZEND_WEAKREF_TAG_MAP); zend_hash_destroy(&wm->ht); zend_object_std_dtor(&wm->std); }