OPcache User Cache Performance Report
Generated at 2026-07-01T16:01:18+00:00. Times are in microseconds; lower is faster.
Environment
| PHP | 8.6.0-dev (cli) |
|---|---|
| Thread Safety | NTS |
| Binary | /workspaces/php-src/sapi/cli/php |
| System | Linux 498c42d87ff4 7.0.11-orbstack-00360-gc9bc4d96ac70 #1 SMP PREEMPT Thu Jun 4 16:40:25 UTC 2026 aarch64 |
| opcache.user_cache_shm_size | 128M |
| Loaded benchmark extensions | zend_opcache, apcu, deepclone |
CLI Repeated Read
| Workload | UserCache | APCu | DeepClone | APCu/UserCache |
|---|---|---|---|---|
constant_ | 0.157 usmedian 0.157 | 2.119 usmedian 2.111 | 2.199 usmedian 2.181 | 13.54x |
route_ | 0.191 usmedian 0.191 | 211.221 usmedian 200.922 | 184.798 usmedian 182.008 | 1,104.64x |
large_ | 0.151 usmedian 0.151 | 90.238 usmedian 89.690 | 90.461 usmedian 89.639 | 597.96x |
large_ | 0.113 usmedian 0.112 | 5.331 usmedian 5.356 | 5.539 usmedian 5.567 | 47.36x |
large_ | 0.177 usmedian 0.178 | 92.972 usmedian 91.569 | 100.508 usmedian 101.000 | 523.85x |
metadata_ | 0.250 usmedian 0.251 | 179.818 usmedian 179.112 | 178.231 usmedian 177.878 | 717.87x |
metadata_ | 0.325 usmedian 0.324 | 175.146 usmedian 174.267 | 179.524 usmedian 179.555 | 539.26x |
safe_ | 0.542 usmedian 0.542 | 2.381 usmedian 2.370 | 4.044 usmedian 4.054 | 4.39x |
spl_ | 0.794 usmedian 0.793 | 19.485 usmedian 19.384 | 20.772 usmedian 20.650 | 24.54x |
spl_ | 1.698 usmedian 1.748 | 19.370 usmedian 19.240 | 20.930 usmedian 20.855 | 11.41x |
spl_ | 1.306 usmedian 1.302 | 31.244 usmedian 30.762 | 31.699 usmedian 31.615 | 23.93x |
carbon_ | 30.369 usmedian 29.744 | 180.527 usmedian 178.848 | 225.110 usmedian 224.546 | 5.94x |
carbon_ | 30.239 usmedian 30.093 | 192.792 usmedian 191.382 | 221.118 usmedian 220.696 | 6.38x |
CLI Store
| Workload | UserCache | APCu | DeepClone | APCu/UserCache |
|---|---|---|---|---|
constant_store trade-off note | 3.136 usmedian 3.135 | 1.258 usmedian 1.255 | 1.319 usmedian 1.321 | 0.40x |
route_store trade-off note | 322.105 usmedian 320.367 | 87.303 usmedian 86.353 | 103.537 usmedian 103.618 | 0.27x |
large_store trade-off note | 167.485 usmedian 164.459 | 49.800 usmedian 49.703 | 64.191 usmedian 64.132 | 0.30x |
large_store trade-off note | 11.083 usmedian 11.077 | 4.318 usmedian 4.315 | 12.919 usmedian 12.876 | 0.39x |
large_store trade-off note | 161.895 usmedian 161.823 | 51.788 usmedian 52.548 | 64.944 usmedian 64.766 | 0.32x |
metadata_store trade-off note | 335.535 usmedian 334.894 | 92.052 usmedian 91.735 | 110.168 usmedian 109.922 | 0.27x |
metadata_store trade-off note | 343.825 usmedian 344.967 | 90.337 usmedian 90.251 | 111.316 usmedian 111.411 | 0.26x |
safe_store trade-off note | 2.933 usmedian 2.919 | 1.212 usmedian 1.206 | 3.255 usmedian 3.250 | 0.41x |
spl_store trade-off note | 35.566 usmedian 35.379 | 11.036 usmedian 11.010 | 16.115 usmedian 16.133 | 0.31x |
spl_store trade-off note | 31.725 usmedian 31.790 | 11.770 usmedian 11.742 | 16.003 usmedian 15.975 | 0.37x |
spl_store trade-off note | 55.895 usmedian 55.773 | 18.768 usmedian 18.656 | 24.118 usmedian 24.076 | 0.34x |
carbon_ | 203.404 usmedian 203.849 | 289.868 usmedian 289.499 | 360.186 usmedian 360.624 | 1.43x |
carbon_ | 225.221 usmedian 224.219 | 317.405 usmedian 316.673 | 380.116 usmedian 380.124 | 1.41x |
FPM One Fetch Per Request
| Workload | UserCache | APCu | DeepClone | APCu/UserCache | Workers |
|---|---|---|---|---|---|
constant_ | 2.292 usmedian 1.500 | 7.607 usmedian 7.042 | 6.813 usmedian 5.896 | 3.32x | 5 |
route_ | 3.910 usmedian 3.542 | 472.488 usmedian 468.500 | 476.440 usmedian 482.104 | 120.85x | 5 |
large_ | 3.756 usmedian 3.521 | 276.874 usmedian 278.208 | 263.452 usmedian 265.270 | 73.71x | 5 |
large_ | 5.743 usmedian 3.584 | 18.456 usmedian 15.271 | 17.801 usmedian 13.959 | 3.21x | 5 |
large_ | 36.849 usmedian 36.230 | 226.388 usmedian 224.166 | 231.226 usmedian 235.521 | 6.14x | 5 |
metadata_ | 101.490 usmedian 94.167 | 474.141 usmedian 492.604 | 359.705 usmedian 323.958 | 4.67x | 5 |
metadata_ | 77.060 usmedian 74.000 | 419.406 usmedian 430.271 | 510.993 usmedian 486.750 | 5.44x | 5 |
safe_ | 16.376 usmedian 13.729 | 24.397 usmedian 20.063 | 28.171 usmedian 25.084 | 1.49x | 5 |
spl_ | 9.303 usmedian 8.125 | 61.867 usmedian 59.500 | 66.477 usmedian 65.959 | 6.65x | 5 |
spl_ | 11.309 usmedian 9.542 | 67.708 usmedian 65.188 | 66.433 usmedian 64.980 | 5.99x | 5 |
spl_ | 7.317 usmedian 6.063 | 104.465 usmedian 103.521 | 110.114 usmedian 108.146 | 14.28x | 5 |
carbon_ | 296.946 usmedian 297.583 | 605.159 usmedian 590.167 | 612.603 usmedian 595.834 | 2.04x | 5 |
carbon_ | 308.572 usmedian 316.979 | 601.078 usmedian 610.396 | 687.385 usmedian 687.917 | 1.95x | 5 |
FPM Hot Read
| Workload | UserCache | APCu | DeepClone | APCu/UserCache | Workers |
|---|---|---|---|---|---|
route_ | 0.462 usmedian 0.401 | 203.533 usmedian 216.544 | 176.848 usmedian 160.294 | 440.51x | 5 |
large_ | 0.223 usmedian 0.212 | 96.877 usmedian 89.921 | 97.985 usmedian 90.478 | 434.99x | 5 |
large_ | 0.165 usmedian 0.137 | 8.595 usmedian 9.564 | 8.967 usmedian 9.570 | 52.06x | 5 |
large_ | 0.576 usmedian 0.573 | 97.342 usmedian 91.601 | 98.284 usmedian 91.729 | 169.06x | 5 |
metadata_ | 0.911 usmedian 0.912 | 189.581 usmedian 173.893 | 200.635 usmedian 194.952 | 208.13x | 5 |
metadata_ | 1.028 usmedian 1.031 | 191.605 usmedian 177.608 | 202.894 usmedian 188.424 | 186.33x | 5 |
safe_ | 0.701 usmedian 0.693 | 2.710 usmedian 2.688 | 4.656 usmedian 4.579 | 3.86x | 5 |
spl_ | 0.940 usmedian 0.927 | 22.786 usmedian 22.016 | 24.844 usmedian 24.100 | 24.24x | 5 |
spl_ | 1.427 usmedian 1.418 | 33.968 usmedian 32.640 | 36.419 usmedian 34.161 | 23.81x | 5 |
carbon_ | 34.331 usmedian 33.028 | 209.218 usmedian 190.512 | 282.133 usmedian 278.857 | 6.09x | 5 |
carbon_ | 44.108 usmedian 42.354 | 273.962 usmedian 264.983 | 354.067 usmedian 357.772 | 6.21x | 5 |
Resident Payload Probe
| Workload | Resident direct access | UserCache fetch + access | Estimated fetch overhead | Resident / UserCache |
|---|---|---|---|---|
constant_resident baseline note | 0.058 us | 0.157 us | 0.098 us | 0.37x |
route_resident baseline note | 0.101 us | 0.191 us | 0.090 us | 0.53x |
large_resident baseline note | 0.068 us | 0.151 us | 0.083 us | 0.45x |
large_resident baseline note | 0.033 us | 0.113 us | 0.079 us | 0.30x |
Bulk Read: 32 Keys
| Backend | Mean/batch | Median/batch | Mean/key |
|---|---|---|---|
user_ | 1.195 us | 1.156 us | 0.037 us |
user_ | 1.986 us | 1.969 us | 0.062 us |
apcu_ | 9.330 us | 9.240 us | 0.292 us |
apcu_ | 10.252 us | 10.257 us | 0.320 us |
Bulk Read: 128 Keys
| Backend | Mean/batch | Median/batch | Mean/key |
|---|---|---|---|
user_ | 4.663 us | 4.587 us | 0.036 us |
user_ | 7.933 us | 7.930 us | 0.062 us |
apcu_ | 39.028 us | 38.712 us | 0.305 us |
apcu_ | 42.742 us | 42.411 us | 0.334 us |
Artifacts
| Artifact | Path |
|---|---|
| CLI repeated read JSON | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/cli-read/user-cache-benchmark-20260701-160044.json |
| CLI write JSON | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/cli-write/user-cache-benchmark-20260701-160059.json |
| Resident probe JSON | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/resident-payload-probe.json |
| FPM one fetch/request JSON | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/fpm-read-once.json |
| FPM hot read JSON | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/fpm-read-hot.json |
| Bulk read JSON #1 | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/bulk-read-32.json |
| Bulk read JSON #2 | /workspaces/php-src/php-opcache_user_cache_benchmark_harness/results/nts-clean-20260701T155740Z/bulk-read-128.json |
Workloads
Click a workload name in the result tables to jump to its description. CLI repeated read stores the payload before timing, then measures only fetch() plus the access probe. CLI store repeatedly stores the payload over the configured key space. Resident direct access runs only the access probe against an already-resident payload. FPM rows run the same fetch path through nginx/php-fpm workers. Bulk read primes multiple keys, then measures fetching the whole key set.
| Workload | What It Measures | Measured In |
|---|---|---|
constant_Constant array | Small immutable constant-array payload.Mutates fetched copy: no | CLI repeated read, CLI store, Resident direct access, FPM one fetch/request |
route_Compiled route table read | Framework-shaped compiled route table and URL generator arrays.Mutates fetched copy: no | CLI repeated read, CLI store, Resident direct access, FPM one fetch/request, FPM hot read |
large_Large nested array | Larger object-free nested array with repeated scalar leaves.Mutates fetched copy: no | CLI repeated read, CLI store, Resident direct access, FPM one fetch/request, FPM hot read |
large_Large immutable string | Large string payload that stresses byte-copy avoidance on repeated reads.Mutates fetched copy: no | CLI repeated read, CLI store, Resident direct access, FPM one fetch/request, FPM hot read |
large_Large direct userland-object graph | Large payload object containing many array rows and a child object.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
metadata_Application metadata object read | Application metadata object with route and service metadata arrays.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
metadata_Application metadata object fetch and mutate | Fetches a cached userland object graph, then mutates the fetched copy.Mutates fetched copy: yes | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
safe_DateTime/DateInterval direct-restore object | DateTimeImmutable, DateTimeZone, and DateInterval safe-direct state.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
spl_SPL collection direct-restore object | ArrayObject, ArrayIterator, RecursiveArrayIterator, and SplFixedArray state.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
spl_SPL linked-list direct-restore object | SplDoublyLinkedList, SplQueue, and SplStack state.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request |
spl_SPL heap direct-restore object | SplMinHeap, SplMaxHeap, and SplPriorityQueue state.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
carbon_Carbon DateTime serializer object | CarbonImmutable/Carbon/CarbonTimeZone object graph used to compare DateTime-derived restore costs.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
carbon_Model with Carbon properties | Dummy model objects with three Carbon properties: createdAt, updatedAt, and publishedAt.Mutates fetched copy: no | CLI repeated read, CLI store, FPM one fetch/request, FPM hot read |
bulk_read_32_keysBulk Read: 32 Keys | Uses the multi-key config payload, primes 32 keys before timing, then repeatedly fetches all keys as a batch. UserCache is measured with fetchMultiple() and with a per-key fetch() loop; APCu is measured with apcu_fetch(array) and with a per-key loop.Measured batches per iteration: 2000 | Bulk read |
bulk_read_128_keysBulk Read: 128 Keys | Uses the multi-key config payload, primes 128 keys before timing, then repeatedly fetches all keys as a batch. UserCache is measured with fetchMultiple() and with a per-key fetch() loop; APCu is measured with apcu_fetch(array) and with a per-key loop.Measured batches per iteration: 800 | Bulk read |
Notes
Already-resident data is a baseline
The resident table does not include UserCache store time. It compares direct access to an already-resident payload with fetching a previously stored UserCache entry and running the same access probe. The difference estimates fetch and materialization overhead after the value has already been stored. The primary comparison for UserCache is against APCu or ext-deepclone when reconstructing object-heavy payloads, not against literals already resident in the request.
Slower stores are an expected trade-off for faster reads
Store workloads are shown to make the write-side cost explicit. APCu-style shared caches are usually used for read-heavy paths, where a stored value is read many times. Store throughput is an intentional trade-off in this design, not the metric it is optimized for.