Skip to content

Commit 6d48411

Browse files
committed
#57: Optimize pointer hashing in async waker events
- Add ptr_to_index() function to improve hash distribution for pointer keys - Replace all (zend_ulong) event casts with ptr_to_index(event) calls - Use zend_hash_index_find_ptr() where appropriate to simplify code - Preserve critical memory management in waker trigger updates The ptr_to_index() function uses right rotation by 3 bits to better distribute pointer addresses as hash keys, reducing collisions caused by zero lower bits from memory alignment.
1 parent 875eadb commit 6d48411

File tree

4 files changed

+29
-19
lines changed

4 files changed

+29
-19
lines changed

coroutine.c

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -241,20 +241,18 @@ static HashTable *async_coroutine_object_gc(zend_object *object, zval **table, i
241241
}
242242

243243
/* Add events HashTable contents */
244-
zval *event_val;
244+
zend_async_event_t *event;
245245
zval zval_object;
246-
ZEND_HASH_FOREACH_VAL(&coroutine->waker.events, event_val)
246+
ZEND_HASH_FOREACH_PTR(&coroutine->waker.events, event)
247247
{
248-
249-
zend_async_event_t *event = (zend_async_event_t *) Z_PTR_P(event_val);
250-
251248
if (ZEND_ASYNC_EVENT_IS_REFERENCE(event) || ZEND_ASYNC_EVENT_IS_ZEND_OBJ(event)) {
252249
ZVAL_OBJ(&zval_object, ZEND_ASYNC_EVENT_TO_OBJECT(event));
253250
zend_get_gc_buffer_add_zval(buf, &zval_object);
254251
}
255252
}
256253
ZEND_HASH_FOREACH_END();
257254

255+
zval *event_val;
258256
/* Add triggered events if present */
259257
if (coroutine->waker.triggered_events) {
260258
ZEND_HASH_FOREACH_VAL(coroutine->waker.triggered_events, event_val)

libuv_reactor.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,7 @@ static void libuv_add_signal_event(int signum, zend_async_event_t *event)
910910
}
911911

912912
// Add event to the list (use pointer address as key)
913-
zend_hash_index_add_ptr(events_list, (zend_ulong) event, event);
913+
zend_hash_index_add_ptr(events_list, async_ptr_to_index(event), event);
914914
}
915915

916916
/* }}} */
@@ -927,7 +927,7 @@ static void libuv_remove_signal_event(int signum, zend_async_event_t *event)
927927
return;
928928
}
929929

930-
zend_hash_index_del(events_list, (zend_ulong) event);
930+
zend_hash_index_del(events_list, async_ptr_to_index(event));
931931

932932
// If no more events for this signal, remove the handler (but check for process events if SIGCHLD)
933933
if (zend_hash_num_elements(events_list) == 0) {
@@ -989,7 +989,7 @@ static void libuv_handle_process_events(void)
989989

990990
// Verify event is still in the HashTable (might have been removed)
991991
if (ASYNC_G(process_events) == NULL ||
992-
zend_hash_index_find_ptr(ASYNC_G(process_events), (zend_ulong) event) == NULL) {
992+
zend_hash_index_find_ptr(ASYNC_G(process_events), async_ptr_to_index(event)) == NULL) {
993993
continue;
994994
}
995995

@@ -1057,7 +1057,7 @@ static void libuv_add_process_event(zend_async_event_t *event)
10571057
}
10581058

10591059
// Add event to the process events list (use pointer address as key)
1060-
zend_hash_index_add_ptr(ASYNC_G(process_events), (zend_ulong) event, event);
1060+
zend_hash_index_add_ptr(ASYNC_G(process_events), async_ptr_to_index(event), event);
10611061
}
10621062

10631063
/* }}} */
@@ -1069,7 +1069,7 @@ static void libuv_remove_process_event(zend_async_event_t *event)
10691069
return;
10701070
}
10711071

1072-
zend_hash_index_del(ASYNC_G(process_events), (zend_ulong) event);
1072+
zend_hash_index_del(ASYNC_G(process_events), async_ptr_to_index(event));
10731073

10741074
// Only remove SIGCHLD handler if no more process events AND no regular signal events for SIGCHLD
10751075
if (zend_hash_num_elements(ASYNC_G(process_events)) == 0) {

scheduler.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -687,11 +687,10 @@ static void async_scheduler_dtor(void)
687687
zval_c_buffer_cleanup(&ASYNC_G(resumed_coroutines));
688688
zval_c_buffer_cleanup(&ASYNC_G(microtasks));
689689

690-
zval *current;
690+
async_coroutine_t *coroutine;
691691
// foreach by fibers_state and release all fibers
692-
ZEND_HASH_FOREACH_VAL(&ASYNC_G(coroutines), current)
692+
ZEND_HASH_FOREACH_PTR(&ASYNC_G(coroutines), coroutine)
693693
{
694-
async_coroutine_t *coroutine = Z_PTR_P(current);
695694
OBJ_RELEASE(&coroutine->std);
696695
}
697696
ZEND_HASH_FOREACH_END();
@@ -714,10 +713,9 @@ static zend_always_inline void start_waker_events(zend_async_waker_t *waker)
714713
{
715714
ZEND_ASSERT(waker != NULL && "Waker is NULL in async_scheduler_start_waker_events");
716715

717-
zval *current;
718-
ZEND_HASH_FOREACH_VAL(&waker->events, current)
716+
zend_async_waker_trigger_t *trigger;
717+
ZEND_HASH_FOREACH_PTR(&waker->events, trigger)
719718
{
720-
const zend_async_waker_trigger_t *trigger = Z_PTR_P(current);
721719
trigger->event->start(trigger->event);
722720
}
723721
ZEND_HASH_FOREACH_END();
@@ -727,10 +725,9 @@ static zend_always_inline void stop_waker_events(zend_async_waker_t *waker)
727725
{
728726
ZEND_ASSERT(waker != NULL && "Waker is NULL in async_scheduler_stop_waker_events");
729727

730-
zval *current;
731-
ZEND_HASH_FOREACH_VAL(&waker->events, current)
728+
zend_async_waker_trigger_t *trigger;
729+
ZEND_HASH_FOREACH_PTR(&waker->events, trigger)
732730
{
733-
const zend_async_waker_trigger_t *trigger = Z_PTR_P(current);
734731
trigger->event->stop(trigger->event);
735732
}
736733
ZEND_HASH_FOREACH_END();

zend_common.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,19 @@ void zend_copy_fci(zend_fcall_info *dest_fci,
267267
zend_fcall_info *src_fci,
268268
zend_fcall_info_cache *src_fcc);
269269

270+
/**
271+
* Optimized pointer to hash index conversion.
272+
*
273+
* Uses right rotation by 3 bits to improve hash distribution for pointer addresses,
274+
* reducing collisions caused by zero lower bits from memory alignment.
275+
*
276+
* @param ptr Pointer to convert to hash index
277+
* @return Optimized hash index value
278+
*/
279+
static zend_always_inline zend_ulong async_ptr_to_index(void *ptr)
280+
{
281+
zend_ulong key = (zend_ulong) ptr;
282+
return (key >> 3) | (key << ((sizeof(key) * 8) - 3));
283+
}
284+
270285
#endif // ASYNC_ZEND_COMMON_H

0 commit comments

Comments
 (0)