Repository navigation
gh-142183: Cache one datachunk per tstate to prevent alloc/dealloc thrashing - #145789
Conversation
repeatedly hitting the same call depth at exactly the wrong boundary.
|
Just to be clear: this is effectively a freelist of 1, and there's still an easily crafted (but much less likely in reality, I would argue) case where two (or more) stack chunks are repeatedly allocated and deallocated. That requires a much larger chain of calls -- or much larger functions -- so it's not as pronounced, but crafting code to hit that exact case isn't hard. It shows a ~15% penalty for being at just the wrong stack depth, compared to 35+% for the single chunk case. I considered making the cached chunk a freelist (which would be easy since they're already a linked list) but this would mean keeping all datastack chunks of a thread alive for the entire duration of a thread, which might not be a good idea. Caching a single chunk seems like a reasonable compromise. Here's some benchmark results using the repro I provided in the issue, run on a not particularly quiet machine so the results are a little noisy. 55 is the stack depth level that triggers the bad case, 56 is one level deeper (so slightly more work) and avoids it: |
markshannon
left a comment
There was a problem hiding this comment.
Looks good. Thanks for fixing this.
Do you want to backport this to 3.14 and maybe 3.13?
Note:
Although this is a solid fix for 3.14 and 31.3, we'll probably want to use a resizable stack for 3.15+ to avoid deopts in the SAI and JIT when operating at the edge of a chunk
|
I think we should backport, yes, unless @hugovk thinks it's a bad idea. |
|
Thanks @Yhg1s for the PR 🌮🎉.. I'm working now to backport this PR to: 3.13, 3.14. |
…loc thrashing (pythonGH-145789) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. --------- (cherry picked from commit 706fd4e) Co-authored-by: T. Wouters <thomas@python.org> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
…loc thrashing (pythonGH-145789) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. --------- (cherry picked from commit 706fd4e) Co-authored-by: T. Wouters <thomas@python.org> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
|
GH-145828 is a backport of this pull request to the 3.14 branch. |
|
GH-145829 is a backport of this pull request to the 3.13 branch. |
…lloc thrashing (GH-145789) (#145828) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. Move new _ts member to the end to not mess up remote debuggers' ideas of the struct's layout. (The struct is only created by the runtime, and the new field only used by the runtime, so it should be safe.) (cherry picked from commit 706fd4e) Co-authored-by: T. Wouters <thomas@python.org> Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
…loc thrashing (pythonGH-145789) (python#145828) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. Move new _ts member to the end to not mess up remote debuggers' ideas of the struct's layout. (The struct is only created by the runtime, and the new field only used by the runtime, so it should be safe.) (cherry picked from commit 706fd4e) (cherry picked from commit 19cbcc0) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
|
GH-146123 is a backport of this pull request to the 3.13 branch. |
Summary: Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. Upstream issue: python/cpython#142183 Upstream PR: python/cpython#145789 Reviewed By: itamaro Differential Revision: D96052502 fbshipit-source-id: 883f6148568b6cca5a71dd7d754eb206fbb56f90
Summary: Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. Upstream issue: python/cpython#142183 Upstream PR: python/cpython#145789 Reviewed By: itamaro Differential Revision: D96052502 fbshipit-source-id: 883f6148568b6cca5a71dd7d754eb206fbb56f90
…lloc thrashing (GH-145789) (#146123) * gh-142183: Cache one datachunk per tstate to prevent alloc/dealloc thrashing (GH-145789) (#145828) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. Move new _ts member to the end to not mess up remote debuggers' ideas of the struct's layout. (The struct is only created by the runtime, and the new field only used by the runtime, so it should be safe.) (cherry picked from commit 706fd4e) (cherry picked from commit 19cbcc0) Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Miss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
…loc thrashing (python#145789) Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
CPython 3.11+ keeps frames in per-thread data-stack chunks. When a hot call sits at a chunk boundary, every call maps a fresh chunk and every return unmaps it (gh-142183). On GB200 each munmap also pays a Grace SMMU invalidation once NCCL has peer-mapped memory, so the churn costs milliseconds per executor step: MiniMax-M3 piecewise CUDA graph prefill steps spent about half their host time in munmap, all on the executor thread. CPython fixed this in 3.13.13 and 3.14.4; 3.12 will not get the fix. Add run_with_frame_stack_reserve() and call PyExecutor.event_loop through it, so the loop starts near the beginning of a large data-stack chunk and ordinary call depths never reach a boundary. The reserve is applied only on affected CPython versions and can be disabled with TLLM_DISABLE_FRAME_STACK_RESERVE=1. Cover the version gate, the disable switch, the churn itself and the executor wiring with cpu_only unit tests. Background: - python/cpython#142183 (the issue) - python/cpython#145789 (the fix; backports #145829 / #146123 for 3.13, #145828 for 3.14) - python/cpython#88756 (3.11 change that moved frames onto the data stack) - sgl-project/sglang-torchtpu#1037 (same technique) Signed-off-by: Dan Hansen <218031328+dhansen-nvidia@users.noreply.github.com>
Cache one datachunk per tstate to prevent alloc/dealloc thrashing when repeatedly hitting the same call depth at exactly the wrong boundary.