[MLX] C++ runner for HF LLMs with the off-graph KV cache - #21683
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21683
Note: Links to docs will display an error until the docs builds have been completed. ❌ 3 New FailuresAs of commit 589d63c with merge base 9958d39 ( NEW FAILURES - The following jobs have failed:
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
|
Can you add something to the readme about this? |
|
@claude review this code |
|
Claude finished @metascroy's task in 6m 49s —— View job Code review — MLX C++ runner for off-graph KV cacheRead 1. Missing
|
| // Usage: | ||
| // run_llm_hf --pte <model.pte> --tokenizer <tokenizer.json> \ | ||
| // [--kv-max-capacity N] [--kv-storage-dtype bf16|fp16|fp32] \ | ||
| // [--kv-initial-capacity N] [--kv-max-write N] \ |
There was a problem hiding this comment.
Why is kv-max-write a param?
Isn't it deriable from prefill-chunk-size?
There was a problem hiding this comment.
There was no chunking, but added --prefill-chunk-size (default 512, overrideable) and dropped --kv-max-write.
There was a problem hiding this comment.
Yeah, we can add a --prefill-chunk-size to export script with default value of 512.
Write the chunk size to metadata that can be read in the runner.
Error out during export if chunk size is bigger than ring size on sliding wndow.
There was a problem hiding this comment.
Updated to this and if in runtime --kv-windows narrows window size it also narrows the chunk with it.
| } // namespace | ||
|
|
||
| int main(int argc, char** argv) { | ||
| const std::string pte = arg(argc, argv, "--pte", ""); |
There was a problem hiding this comment.
Changed to gflags.
| } | ||
|
|
||
| if (off_graph) { | ||
| cache::CacheConfig cfg{}; |
There was a problem hiding this comment.
It's just a demo, but can we extract some of this cache config construction from pte metadata into a helper?
|
|
||
| // argmax over the last position's vocab row of a [1, T, vocab] logits tensor, | ||
| // reading whatever float dtype the op emitted. | ||
| int64_t argmax_last(const ::executorch::aten::Tensor& logits) { |
There was a problem hiding this comment.
Don't we have a sampler/runner in ExecuTorch extension llm?
See llama runner and some of the other runners we have.
There was a problem hiding this comment.
Changed it to reuse extension.
| const double pf_t = prompt_len / (pf / 1000.0); | ||
| const double dc_t = dc > 0 ? generated / (dc / 1000.0) : 0.0; | ||
| std::cout << "\n[iter " << iter << (measured ? "" : " warmup") | ||
| << "] prefill " << pf_t << " tok/s (" << prompt_len << " tok, " |
There was a problem hiding this comment.
I think we have something that collects stats on running?
Can you see some of the other C++ runners and reuse existign abstractions?
There was a problem hiding this comment.
Switched to the shared Stats/print_report, removed --iters and changed --warmup to a bool to match the other runners and be able to use stats directly. Additionally adopted load_tokenizer and get_eos_ids abstractions.
dd06901 to
977de44
Compare
|
The MLX LLM jobs skip on fork PRs (they need HF secrets), is there anything I can do to run those jobs? |
|
@claude review this code |
|
Claude finished @metascroy's task in 5m 56s —— View job Code review — MLX C++ runner for off-graph KV cache (re-review)Re-read Resolved since last review
Minor / nits (non-blocking)
Confirmed correct
Nothing here blocks. #1 is the only item I'd bother touching before merge, and it's a comment/label fix. |
You should be able to add the "ciflow/mlx" label to trigger the MLX jobs. I just added it. |
|
I removed the kwargs check, HuggingFace forwards model-level arguments into attention (gemma 4 sends labels), so refusing anything unrecognised broke its export. |
Summary
This runner builds an MLXSequenceCache, installs it, and passes the cache key, so it's the run path for .pte files exported with --use-offgraph-cache. The cache's shape is read from the .pte metadata. The flags left are policy the model can't imply: --kv-max-capacity, --kv-storage-dtype, --kv-initial-capacity, --kv-max-write, --kv-windows.
Depends on #21680; the new CI job fails until that lands.
Files
Test
CI builds the runner, then for llama-1b, gemma3-1b and gemma4-e2b exports off-graph and asserts the same "Paris" answer test-mlx-llm checks.