Fix CLFUS RAM cache value metric broken by integer division#13233
Fix CLFUS RAM cache value metric broken by integer division#13233phongn wants to merge 3 commits into
Conversation
PR apache#11733 rewrote the CACHE_VALUE_HITS_SIZE cast so static_cast<float> wraps the whole quotient, making (hits + 1) / (size + overhead) integer division. It truncates to 0 for normal object sizes, zeroing the value metric and collapsing CLFUS to FIFO: no promote-on-hit, no clock second chance, and no value-based ghost re-admission. Bind the cast to the numerator to restore floating-point division, and add the ram_cache_clfus_value regression test as a guard (it fails on the pre-fix macro and passes after). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes a correctness regression in the CLFUS RAM cache eviction/admission “value density” metric by restoring floating-point division in CACHE_VALUE_HITS_SIZE, and adds a focused regression test to prevent the metric from silently collapsing to zero again.
Changes:
- Fix
CACHE_VALUE_HITS_SIZEso the cast applies to the numerator, ensuring floating-point division. - Add
ram_cache_clfus_valueregression test to validate the value metric is non-zero and monotonic with hits and size.
moonchen
left a comment
There was a problem hiding this comment.
Change looks good. One optional suggestion:
in your regression test, you could use static_assert(CACHE_VALUE_HITS_SIZE(1u, 16384u) > 0.0f, ...) and similar statements, so that the failures are caught at compile time without running the test.
A backport would be ideal, I'm seeing the same behavior 🙏 |
I took the liberty of replacing the old C/C++ preprocessor macros and constants with modern |
moonchen
left a comment
There was a problem hiding this comment.
👍 to converting macros to constexpr. Just a couple of artifacts from the conversion.
Summary
The CLFUS RAM cache ranks objects by a value density,
value = (hits + 1) / (size + overhead), compared against a runningdoubleaverage on every eviction/admission decision. Since #11733 this has been computed with integer division and truncates to0for essentially every object, silently disabling CLFUS's frequency/size/recency logic and degrading it to FIFO.Root cause
#11733 ("Fix C-style cast in Cache subsystem") modernized the macro:
The original C cast bound only to the numerator, so the division was floating point. The
static_cast<float>(...)wraps the whole quotient, so(hits + 1) / (size + overhead)is now integer division.hitsis on the order of tens whilesize + overheadis thousands+, so the result truncates to0for any normal object.With
value ≡ 0,_average_valuestays0and every value-gated branch is dead (0 > 0is false):get()),put()),CLFUS effectively becomes FIFO, which generally performs worse than the simpler LRU it is meant to beat. This has affected every CLFUS deployment since the regression merged on 2024-08-28.
Fix
Bind the cast to the numerator only, restoring floating-point division (the pre-#11733 semantics). In addition, convert to modern C++
constexprfor type-safety:Test
Adds
ram_cache_clfus_value, a focused regression test asserting the value metric is a non-zero fraction and is monotonic in hits and in size. It fails on the pre-fix macro and passes after:FAILED("value metric truncated to zero", etc.)PASSEDThe existing
ram_cacheregression still passes. With the fix, CLFUS's size-aware behavior is restored — in the bundledram_cachetest at the 16 MB size, its variable-size hit rate rises to ~0.82, slightly ahead of LRU (~0.80) on the same workload.Drive-By Changes
@moonchen suggested that the new regression test could be run at compile time via use of
static_assert; to facilitate this change the prior C/C++ preprocessor macros and constants have been converted to modern C++constexprconstants and functions.Notes
proxy.config.cache.ram_cache.algorithm = 0(CLFUS) since Fix C-style cast in Cache subsystem #11733; a backport may be warranted.Fixes a regression introduced in #11733.