Skip to content

Share the method's runtime cache with fake closures of the same scope - #23291

Open
staabm wants to merge 1 commit into
php:masterfrom
staabm:closure
Open

Share the method's runtime cache with fake closures of the same scope#23291
staabm wants to merge 1 commit into
php:masterfrom
staabm:closure

Conversation

@staabm

@staabm staabm commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

disclaimer: this change was generated by claude opus. I have little experience with php-src development


Creating a fake closure (Closure::fromCallable(), first-class callable syntax, ReflectionMethod::getClosure()) over a method whose runtime cache was not yet initialized allocated a cold per-closure heap cache on every creation. Initialize the method's own shared cache instead - exactly what the first real call would do - so repeated closure creations reuse one warm cache. Trampolines and heap-cache functions keep the per-closure path.


after this PR

➜  php-src git:(closure) ✗ hyperfine 'sapi/cli/php -n -d opcache.enable_cli=0 closure_from_callable_runtime_cache_bench.php'
Benchmark 1: sapi/cli/php -n -d opcache.enable_cli=0 closure_from_callable_runtime_cache_bench.php
  Time (mean ± σ):     136.5 ms ±   1.4 ms    [User: 132.8 ms, System: 2.3 ms]
  Range (min … max):   133.4 ms … 138.7 ms    21 runs

before this PR

➜  php-src git:(closure) ✗ hyperfine 'sapi/cli/php_old -n -d opcache.enable_cli=0 closure_from_callable_runtime_cache_bench.php'
Benchmark 1: sapi/cli/php_old -n -d opcache.enable_cli=0 closure_from_callable_runtime_cache_bench.php
  Time (mean ± σ):     150.6 ms ±   1.3 ms    [User: 147.2 ms, System: 2.3 ms]
  Range (min … max):   148.3 ms … 152.9 ms    19 runs

benchmark closure_from_callable_runtime_cache_bench.php: https://gist.github.com/staabm/20c082674ed880c8fc097b4775f2d7a7

Creating a fake closure (Closure::fromCallable(), first-class callable
syntax, ReflectionMethod::getClosure()) over a method whose runtime
cache was not yet initialized allocated a cold per-closure heap cache
on every creation. Initialize the method's own shared cache instead -
exactly what the first real call would do - so repeated closure
creations reuse one warm cache. Trampolines and heap-cache functions
keep the per-closure path.
staabm referenced this pull request in ondrejmirtes/php-src Aug 15, 2026
Creating a fake closure (Closure::fromCallable(), first-class callable
syntax, ReflectionMethod::getClosure()) over a method whose runtime
cache was not yet initialized allocated a cold per-closure heap cache
on every creation. Initialize the method's own shared cache instead -
exactly what the first real call would do - so repeated closure
creations reuse one warm cache. Trampolines and heap-cache functions
keep the per-closure path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants