Repository navigation
Conversation
The optimized KDA prefill op keeps the scratch tensors of the last 8 prefill shapes in _buf_cache (LRU). It wraps that scratch with _ct_cached, whose wrappers live in _input_wrap_cache and pin the tensor storage. _prune_on_gc removes a wrapper only when its tensor is garbage-collected, which cannot happen while the wrapper pins it. So the scratch of every evicted shape stays allocated. A benchmark with a fixed input length never evicts. A serving process whose prefill length changes on every call does: with GLM-5.3-Flash agent traffic on 4x B200 (TP4, EP4, 16 requests in flight, prompts near 100K tokens), GPU memory in use grew by about 1.75 GiB per minute on every rank, and the server stopped with CUDA out of memory after 20 to 25 minutes. A tensor census showed six BF16 tensors of shape [1, T, 16, 128] left behind for each distinct prefill length T. _get_buffers now drops the wrappers of an entry when _buf_cache evicts it. With the change, memory in use stays flat under the same traffic. Co-Authored-By: Claude Code <noreply@anthropic.com>
1 task done
Collaborator
|
Superseded by #19854 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
This change fixes a GPU memory leak in the optimized KDA prefill op (
tensorrt_llm/_torch/custom_ops/cute_dsl_kimi_k3_custom_ops.py). Without the change, a server whose prefill length changes on almost every call grew from 142.8 GiB to 178.2 GiB of GPU memory in use per GPU in 20 minutes. Then it stopped withCUDA out of memory. With the change, a 44-minute run finished 897 calls with 0 failed calls, and the memory in use stayed at 144 GiB.Pull request 19724 of this repository is older, is open, and fixes the same leak in another way (it keeps the wrappers inside the
_buf_cacheentry), so this draft may be closed in its favor.Cause.
_get_bufferskeeps the scratch tensors of the last 8 prefill shapes in_buf_cache(LRU)._launch_fused_k123_invwraps seven of those tensors with_ct_cached. The wrappers live in_input_wrap_cacheand pin the tensor storage._prune_on_gcremoves a wrapper only when its tensor is garbage-collected, and that cannot happen while the wrapper pins the tensor. So the seven tensors of every evicted entry stay allocated. A benchmark with a fixed input length never evicts an entry. A serving process with a new prefill length on almost every call evicts entries all the time.Change.
_get_buffersnow drops the_ct_cachedwrappers of an entry when_buf_cacheevicts it (new helper_release_scratch_wrappers).Measurement. One server on 4x B200 (TP4, EP4, MTP) with an FP8 GLM-5.3-Flash checkpoint and an agent workload: multi-turn sessions with prompts near 100K tokens, where almost every call has a new prefill length. The memory values are GPU memory in use per GPU.
free_gpu_memory_fraction: 0.6,--max_num_tokens 8192, MTP draft length 3CUDA out of memoryin the sparse prefill attention after 485 callsfree_gpu_memory_fraction: 0.5,--max_num_tokens 16384, MTP draft length 4 with rejection samplingNo run without the change started with both the server settings and the client load of the run with the change. The second row has the same settings at a lower load. That server got 16 requests in flight only after the growth of the second row, and I have no memory values for that part.
In the first run, the KV cache blocks in use stayed between 15K and 34K of 81,688 during the growth, so the KV cache did not fill up. A tensor census in a debug run showed BF16 scratch tensors of shape
[1, T, 16, 128]left behind for each distinct prefill lengthT. An earlier run withfree_gpu_memory_fraction: 0.75and--max_num_tokens 16384stopped with the same error after 6 minutes and 204 calls.Expected improvement and risk
_get_buffersreleases the_ct_cachedwrappers of each entry that_buf_cacheevictsCUDA out of memoryafter 20 minutes (other server settings, see the table above). I expect no change in output and no measurable change in speed. I measured neither._buf_cacheevicts an entry. The comment on_buf_cachesays that the LRU bound exists so that scratch is not pinned forever. The wrapper dict inside an entry already leaves with the entry. This change does the same for the wrappers that_ct_cachedkeeps outside the entry._BUF_CACHE_MAX_ENTRIES) never runs it. The cost is one scan of_input_wrap_cachefor each eviction.Test Coverage
tests/unittest/_torch/modules/kimi_kda/test_kda_cache_soundness.py::test_evicted_prefill_scratch_is_released. The test runs the indexed prefill with more distinct lengths than the buffer cache holds. Then it checks that no cached wrapper pins an evicted scratch tensor. On B200 it fails without the change ("28 evicted scratch tensors are still pinned by cached wrappers", which is 4 evictions of 7 tensors each) and passes with the change.tests/integration/test_lists/test-db/l0_b200.yml.v1.3.0rc29) is the same as the file at the base of this pull request. I checked the output of the serving run only lightly: answers are sensible and tool calls parse.To verify, run the new test on a Blackwell GPU (SM100 or SM103) with the
flapackage installed:To see the leak in serving, send prompts whose prefill length changes on every call to a model that uses the optimized KDA prefill, and watch the GPU memory in use.
Not verified
tests/unittest/_torch/modules/kimi_kda/and the other GPU and integration tests of the repository. No upstream CI has run on this pull request.PR Checklist
Please review the following before submitting your PR:
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
🤖 Generated with Claude Code