Skip to content

Commit 2611e4b

Browse files
committed
Optimize the destructor of WeakMap for large WeakMaps
Postpone calling any destructors of entries within the weak map itself until after the singleton `EG(weakmap)` is updated to remove all keys in the weak map. Before, zend_weakref_unregister would do two hash table lookups when freeing an entry from a WeakMap - Free from `EG(weakrefs)` if that was the last reference - zend_weakref_unref_single to remove the entry from the WeakMap itself After this change, only the first hash table lookup is done. The freeing of entries from the weak map itself is done sequentially in `zend_hash_destroy` without hashing or scanning buckets. It may be a good idea to prevent modification of a WeakMap after the weak map starts to get freed. Closes GH-7672
1 parent baeba4b commit 2611e4b

File tree

1 file changed

+15
-8
lines changed

1 file changed

+15
-8
lines changed

Zend/zend_weakrefs.c

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ static void zend_weakref_register(zend_object *object, void *payload) {
109109
&EG(weakrefs), obj_addr, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_HT));
110110
}
111111

112-
static void zend_weakref_unregister(zend_object *object, void *payload) {
112+
static void zend_weakref_unregister(zend_object *object, void *payload, bool weakref_free) {
113113
zend_ulong obj_addr = (zend_ulong) object;
114114
void *tagged_ptr = zend_hash_index_find_ptr(&EG(weakrefs), obj_addr);
115115
ZEND_ASSERT(tagged_ptr && "Weakref not registered?");
@@ -122,7 +122,9 @@ static void zend_weakref_unregister(zend_object *object, void *payload) {
122122
GC_DEL_FLAGS(object, IS_OBJ_WEAKLY_REFERENCED);
123123

124124
/* Do this last, as it may destroy the object. */
125-
zend_weakref_unref_single(ptr, tag, obj_addr);
125+
if (weakref_free) {
126+
zend_weakref_unref_single(ptr, tag, obj_addr);
127+
}
126128
return;
127129
}
128130

@@ -141,8 +143,10 @@ static void zend_weakref_unregister(zend_object *object, void *payload) {
141143
}
142144

143145
/* Do this last, as it may destroy the object. */
144-
zend_weakref_unref_single(
145-
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
146+
if (weakref_free) {
147+
zend_weakref_unref_single(
148+
ZEND_WEAKREF_GET_PTR(payload), ZEND_WEAKREF_GET_TAG(payload), obj_addr);
149+
}
146150
}
147151

148152
ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pData) {
@@ -156,7 +160,7 @@ ZEND_API zval *zend_weakrefs_hash_add(HashTable *ht, zend_object *key, zval *pDa
156160
ZEND_API zend_result zend_weakrefs_hash_del(HashTable *ht, zend_object *key) {
157161
zval *zv = zend_hash_index_find(ht, (zend_ulong) key);
158162
if (zv) {
159-
zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP));
163+
zend_weakref_unregister(key, ZEND_WEAKREF_ENCODE(ht, ZEND_WEAKREF_TAG_MAP), 1);
160164
return SUCCESS;
161165
}
162166
return FAILURE;
@@ -245,7 +249,7 @@ static void zend_weakref_free(zend_object *zo) {
245249
zend_weakref *wr = zend_weakref_from(zo);
246250

247251
if (wr->referent) {
248-
zend_weakref_unregister(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF));
252+
zend_weakref_unregister(wr->referent, ZEND_WEAKREF_ENCODE(wr, ZEND_WEAKREF_TAG_REF), 1);
249253
}
250254

251255
zend_object_std_dtor(&wr->std);
@@ -293,8 +297,11 @@ static void zend_weakmap_free_obj(zend_object *object)
293297
zend_weakmap *wm = zend_weakmap_from(object);
294298
zend_ulong obj_addr;
295299
ZEND_HASH_MAP_FOREACH_NUM_KEY(&wm->ht, obj_addr) {
300+
/* 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).
301+
* Let freeing the corresponding values for WeakMap entries be done in zend_hash_destroy, freeing objects sequentially.
302+
* The performance difference is notable for larger WeakMaps with worse cache locality. */
296303
zend_weakref_unregister(
297-
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
304+
(zend_object *) obj_addr, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 0);
298305
} ZEND_HASH_FOREACH_END();
299306
zend_hash_destroy(&wm->ht);
300307
zend_object_std_dtor(&wm->std);
@@ -395,7 +402,7 @@ static void zend_weakmap_unset_dimension(zend_object *object, zval *offset)
395402
return;
396403
}
397404

398-
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP));
405+
zend_weakref_unregister(obj_key, ZEND_WEAKREF_ENCODE(&wm->ht, ZEND_WEAKREF_TAG_MAP), 1);
399406
}
400407

401408
static int zend_weakmap_count_elements(zend_object *object, zend_long *count)

0 commit comments

Comments
 (0)