Add off-graph KV-cache SequenceCache and flat policy - #21412
Add off-graph KV-cache SequenceCache and flat policy#21412kiymetakdemir wants to merge 1 commit into
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21412
Note: Links to docs will display an error until the docs builds have been completed. ⏳ No Failures, 139 PendingAs of commit a7438ce with merge base 739fe55 ( This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
|
@claude review this code |
|
Claude finished @metascroy's task in 3m 47s —— View job Code Review — Off-graph KV-cache
|
| // registry owns a cache as a CacheBase* -- an opaque, cache-agnostic anchor -- | ||
| // and hands it back by key. The typed faces a runner and backend use to drive | ||
| // the cache are added by the concrete cache implementation. | ||
| // Neutral, tensor-free, ET-independent KV-cache core shared across backends. A |
There was a problem hiding this comment.
Where is the cpp impl file?
| int n_write; | ||
| Run read[2]; | ||
| int n_read; | ||
| int read_base_pos; |
There was a problem hiding this comment.
Is this always read[0].start? If so, does it need to be another field?
There was a problem hiding this comment.
read[0].start is a physical index, read_base_pos is the logical sequence position. In flat it equals read[0].start, but in ring it will be the logical window start (length - window)
| public: | ||
| virtual ~SequencePlanner() = default; | ||
| virtual std::optional<SeqStepPlan> plan(int layer, int position, int T) = 0; | ||
| virtual int depth(int layer) const = 0; // physical rows to allocate for layer |
There was a problem hiding this comment.
It is how many physical rows the backend should allocate for that layer's KV pool, it is capacity for flat but window for ring.
| // Oldest logical position still retained given the current length (0 for a | ||
| // full-history policy; a windowing policy retains only its last window). Used | ||
| // to bound rewind. | ||
| virtual int retained_from(int length) const = 0; |
There was a problem hiding this comment.
Why does this take length?
There was a problem hiding this comment.
retained_from returns the oldest logical position the cache still holds at the current length, it is 0 for full-history flat, but will be length - window for a windowing policy
| explicit FlatPolicy(int capacity) : capacity_(capacity) {} | ||
|
|
||
| int depth() const override { | ||
| return capacity_; |
There was a problem hiding this comment.
I still don't follow what depth is. If it's related to specific memory layout (and not logical bookkeeping), let's leave it out here
There was a problem hiding this comment.
Yes it is, okay, I will remove it.
7d33dff to
a7438ce
Compare
Summary
Adds the neutral single-sequence cache: a SequenceCache controller that owns one logical length for the whole model and produces per-layer, integer-only layout plans, plus its full-history FlatPolicy. Backend and tensor-free the cache logic stays ET-independent, with a thin adapter for ExecuTorch callers. The SeqStepPlan and config shapes are already general for sliding-window policies, but only full-history flat is implemented here.
Files
per-layer plan dispatch with policy dedup) and FlatPolicy (full-history layout). Header-only, ET-independent.
Error/Result (et::plan → OutOfResources, et::rewind → InvalidArgument).
builder registry now keyed by std::pair<backend_id, kind> instead of a concatenated string.
recovery, the registry with a real SequenceCache, and the ET adapter.
Testing
Added C++ unit tests covering the flat bookkeeping (plan/can_extend/rewind/clear), face recovery, the registry with
a real cache, and the ET adapter's result/code mapping. Built and ran through the standard cmake/ctest flow, all
pass:
cmake -B cmake-out -DEXECUTORCH_BUILD_EXTENSION_LLM=ON -DEXECUTORCH_BUILD_TESTS=ON
cmake --build cmake-out --target extension_llm_cache_test -j
ctest --test-dir cmake-out -R extension_llm_cache --output-on-failure