From 241f72a438a75754171b82e9cb81aafc8886d327 Mon Sep 17 00:00:00 2001 From: Florian Engelhardt Date: Mon, 27 Jul 2026 16:39:30 +0200 Subject: [PATCH] perf(profiling): avoid FFI call on allocation hot path Read executor_globals.current_execute_data directly on NTS builds instead of calling through the C FFI wrapper for every allocation and reallocation. Keep the existing wrapper on ZTS builds. A 60-second macOS sample reduced get_current_execute_data self time from 0.412% (207/50,210 main-thread samples) to 0.002% (1/49,737). Known allocation-hook self time fell from 4.184% to 3.601%, a 13.9% relative reduction. Repeated end-to-end throughput remained within system scheduling noise. Validation: cargo test (22 passed); allocation sampling-distance, memory-peak, and GC PHPTs passed. --- profiling/src/allocation/allocation_ge84.rs | 7 ++++--- profiling/src/allocation/allocation_le83.rs | 7 ++++--- profiling/src/allocation/mod.rs | 9 +++++++++ 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/profiling/src/allocation/allocation_ge84.rs b/profiling/src/allocation/allocation_ge84.rs index 606f32df14..80670585aa 100644 --- a/profiling/src/allocation/allocation_ge84.rs +++ b/profiling/src/allocation/allocation_ge84.rs @@ -1,5 +1,6 @@ use crate::allocation::{ - allocation_profiling_stats_should_collect, collect_allocation, untrack_allocation, + allocation_profiling_stats_should_collect, collect_allocation, current_execute_data, + untrack_allocation, }; use crate::bindings as zend; use crate::PROFILER_NAME; @@ -306,7 +307,7 @@ unsafe fn alloc_prof_malloc_impl(len: size_t) -> *mut c_void { // during startup, minit, rinit, ... current_execute_data is null // we are only interested in allocations during userland operations - if zend::ddog_php_prof_get_current_execute_data().is_null() { + if current_execute_data().is_null() { return ptr; } @@ -505,7 +506,7 @@ unsafe fn alloc_prof_realloc_no_untrack_impl(prev_ptr: *mut c_void, len: size_t) unsafe fn alloc_prof_realloc_sample(ptr: *mut c_void, len: size_t) -> *mut c_void { // during startup, minit, rinit, ... current_execute_data is null // we are only interested in allocations during userland operations - if zend::ddog_php_prof_get_current_execute_data().is_null() { + if current_execute_data().is_null() { return ptr; } diff --git a/profiling/src/allocation/allocation_le83.rs b/profiling/src/allocation/allocation_le83.rs index c36de739e9..12a1b37c58 100644 --- a/profiling/src/allocation/allocation_le83.rs +++ b/profiling/src/allocation/allocation_le83.rs @@ -1,5 +1,6 @@ use crate::allocation::{ - allocation_profiling_stats_should_collect, collect_allocation, untrack_allocation, + allocation_profiling_stats_should_collect, collect_allocation, current_execute_data, + untrack_allocation, }; use crate::bindings::{ self as zend, datadog_php_install_handler, datadog_php_zif_handler, @@ -300,7 +301,7 @@ unsafe extern "C" fn alloc_prof_malloc(len: size_t) -> *mut c_void { // during startup, minit, rinit, ... current_execute_data is null // we are only interested in allocations during userland operations - if zend::ddog_php_prof_get_current_execute_data().is_null() { + if current_execute_data().is_null() { return ptr; } @@ -431,7 +432,7 @@ unsafe fn alloc_prof_realloc_no_untrack_impl(prev_ptr: *mut c_void, len: size_t) unsafe fn alloc_prof_realloc_sample(ptr: *mut c_void, len: size_t) -> *mut c_void { // during startup, minit, rinit, ... current_execute_data is null // we are only interested in allocations during userland operations - if zend::ddog_php_prof_get_current_execute_data().is_null() { + if current_execute_data().is_null() { return ptr; } diff --git a/profiling/src/allocation/mod.rs b/profiling/src/allocation/mod.rs index 9d4f46d53c..20f4d2280a 100644 --- a/profiling/src/allocation/mod.rs +++ b/profiling/src/allocation/mod.rs @@ -39,6 +39,15 @@ pub(crate) unsafe fn get_zend_mm_state() -> *mut Cell { ptr::addr_of_mut!((*globals).zend_mm_state) } +#[inline(always)] +pub(crate) unsafe fn current_execute_data() -> *mut zend::zend_execute_data { + #[cfg(not(php_zts))] + return ptr::addr_of!(zend::executor_globals.current_execute_data).read(); + + #[cfg(php_zts)] + zend::ddog_php_prof_get_current_execute_data() +} + /// Macros for accessing ZendMMState from PHP globals. /// These are shared between PHP 8.3- and 8.4+ implementations. /// They are exported at the crate root and can be used in submodules.