[MLX] Per-token positions and a shared pool - #22037
Conversation
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/22037
Note: Links to docs will display an error until the docs builds have been completed. ✅ You can merge normally! (6 Unrelated Failures)As of commit 38e6b21 with merge base bd8b2be ( BROKEN TRUNK - The following jobs failed but were present on the merge base:👉 Rebase onto the `viable/strict` branch to avoid these failures
This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
| @@ -0,0 +1,113 @@ | |||
| /* | |||
There was a problem hiding this comment.
This is just a move of the code that used to be in backends/mlx/runtime/MLXSequenceCache.h, right?
| switch (pos.dtype()) { | ||
| case ::mlx::core::int32: | ||
| position = pos.data<int32_t>()[0]; | ||
| positions = pos.data<int32_t>(); |
There was a problem hiding this comment.
This memory isn't owned by us. What prevents pointer being bad?
| AttendSpec spec = st.cache->update_and_fetch( | ||
| *n.layer_id, | ||
| position, | ||
| positions, |
There was a problem hiding this comment.
Why the raw pointer? Can we pass a const vec ref?
| "update_and_attend: position must hold one entry per query token"); | ||
| } | ||
| // int32 is passed straight through; only an int64 input needs narrowing. | ||
| std::vector<int32_t> int32_positions; |
There was a problem hiding this comment.
Maybe always cp to positions vec to control the memory?
| eval(pos); | ||
| int position; | ||
| const int length = static_cast<int>(pos.size()); | ||
| if (length != static_cast<int>(st.const_tensor_ref(n.k).shape(2))) { |
| break; | ||
| case ::mlx::core::int64: | ||
| position = static_cast<int>(pos.data<int64_t>()[0]); | ||
| int32_positions.resize(static_cast<size_t>(length)); |
There was a problem hiding this comment.
This is the code block where you had a perf regressin before. Did you verify that these changes don't cause regression?
|
@claude review this code |
|
Claude finished @metascroy's task in 1m 52s —— View job Code reviewReviewed the diff against Answers to your inline questions
So lifetime is fine. The real latent hazard is contiguity, not ownership: assert(pos.flags().row_contiguous);
Other observations
Nothing blocking. The one change I'd actually make is the |
Summary
Prepares the MLX byte layer to take a second cache implementation. No behaviour changes.
MLXCache::update_and_fetchtakes per-token positions instead of a run start.The position tensor already carries one entry per query token and the interpreter was reading element
[0]and discarding the rest, so this stops truncation.Poolmoves to its own header and takes(start, len)instead ofcache::Run, so it no longer depends on the neutral cache header.Files
backends/mlx/runtime/MLXCache.h— take per-token positions.backends/mlx/runtime/MLXInterpreter.h—evaland the dtype switch keep their shape; int32 now passes the tensor's own buffer with no copy, only int64 is narrowed, andpos.size()is checked againstk.shape(2).backends/mlx/runtime/MLXPool.h— new;Pool, unchanged apart from the signature.backends/mlx/runtime/MLXSequenceCache.h—run_start, and the fivePoolcall sites.backends/mlx/test/mlx_sequence_cache_test.cpp— astep()helper builds the run fromk.shape(2)Testing
ctest --test-dir cmake-out -R mlx_sequence_cache