Repository navigation
Conversation
… it; docs/performance.md Parallel replay (RX_VK_PARALLEL) is slower than the serial path, and its log said only that the workers took one lock 739 times a command buffer and waited 3.65 ms. That cannot be acted on. Now: - ParLock records the file and line it is taken at (__builtin_FILE/LINE, no call site changed) and how long it waited; par_sites_report logs the sites that took it most with the parallel line. Nothing of it runs while parallel replay is off - the two busiest sites count why: gpu_buffer_bind's pool lookup (a written binding, this thread's cache missing, the mirror state: memory_pool_lock_reasons) and the texture touch's slow path (an upload in flight, a texture not uploaded, the epoch's first check: texture_touch_misses) - docs/performance.md: where the frame goes (CPU-bound in recording), the parallel replay's measurements, and what the sites show: for Ocarina the fixed cost per command buffer, not the lock, is the main cost tests/direct_buffer_writes_test.cpp declares the counters gpu_buffer_bind now keeps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 12, 2026
Joey-Magnus
reviewed
Nov 12, 2026
Joey-Magnus
left a comment
Contributor
There was a problem hiding this comment.
I merged this into current main and ran tests/test_direct_buffer_writes.py and the vk, replay and memory subset of run_all with the Vulkan headers. All pass. par_sites is only written under par_mu and reset after par_active goes false, so it is safe. Three points:
- The description says none of it runs with parallel replay off, but two of the counters do. The fetch_add in gpu_buffer_bind (vk_memory.cpp:3014-3017) and the touch_miss_* ones in texture_touch_quick (vk_texture.cpp:3543, 3547) run on every miss in the serial path, and with parallel on they are shared atomics contended by all workers, which skews the waits you are measuring. Gating them on par_active.load(relaxed) would make the claim true.
- docs/performance.md overlaps docs/cpu-profile.md ("Where the CPU goes", generated by scripts/cpu_report.py). Please fold this into it as a section, or link the two, so there is one place for this.
- #2781 removes the dry pass for short command buffers, which is the per-buffer fixed cost this doc names as the main cost. The two also conflict in the 240-buffer logf block of replay_handle, so the doc's parallel tables will need a re-measure once one of them lands.
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.
RX_VK_PARALLELis slower than the serial path, and its log only said the workers took one lock 739 times per command buffer and waited 3.65 ms, which nobody can act on. This PR counts the lock by site and records why its two busiest sites take it, anddocs/performance.mdwrites down what that showed. It was split out of #1834, as Joey-Magnus suggested, and builds on its own on main.What it does
ParLockby site (vk_internal.h,vk_replay.cpp):ParLockrecords the file and line it's taken at, using defaulted__builtin_FILE()/__builtin_LINE()arguments, so no call site changes. It also records how long each site waited.par_sites_report()logs the busiest sites next to the existing parallel line. None of it runs while parallel replay is off.gpu_buffer_bind's pool lookup (vk_memory.cpp,memory_pool_lock_reasons): a written binding, a thread-cache miss, or the mirror state.vk_texture.cpp,texture_touch_misses): an upload in flight, a texture never uploaded, or the epoch's first check.docs/performance.mdrecords where the frame goes (CPU-bound in recording), the parallel replay's measurements against serial, and what the sites show. For Ocarina, the fixed cost per command buffer is the main cost, not the lock. It names functions, not line numbers.tests/direct_buffer_writes_test.cppdeclares the countersgpu_buffer_bindnow keeps.The NVIDIA shader-cache default that #1834 had isn't in here. As noted on #1834, that would be its own PR and a maintainer decision.
Testing
vk_texture.cpp,vk_memory.cpp,vk_replay.cppandvk_draw.cppcompile with clang usingnx2/build.py's renderer flags.tests/run_all.py, the whole suite, locally: 433 passed. The failures are exactly the ones main at 51da046 has on this machine, all down to the local toolchain.docs/performance.mdare from Linux, an RTX 5060 Ti, headless, in Kokiri Forest, as the doc says.🤖 Generated with Claude Code