Profile the LBVH build and detect stages on both the CPU and the GPU - #263
Merged
Merged
Conversation
ipc::LBVH's build was already instrumented; ipc::cuda::LBVH had no profiler coverage at all, and neither broad phase split its detection into traversal and candidate output. - `ipc::cuda::CudaEventTimer` (`utils/cuda/cuda_profiler.cuh`) is a `ProfilePoint` timer backed by CUDA events, so a device stage is measured by when the device ran it rather than when the host returned from the launch. `IPC_TOOLKIT_PROFILE_BLOCK_CUDA` is its block macro. - `ipc::cuda::LBVH` gains blocks for the transfers, the box kernels, the domain reduction, and each tree's Morton, sort, hierarchy, and patch stages, plus parent scopes on `build()` and `detect_host()` so time not attributed to a stage is visible as a remainder rather than lost. - `materialize()` separates the host staging allocation, the device-to-host copy, and the `Candidate` construction; `ipc::LBVH::detect_candidates()` separates the traversal from the thread-local merge. - `benchmark_lbvh_stages.cu` drives both broad phases over the six benchmark scenes and writes the profiler tree as JSON, following the `IPC_TOOLKIT_BENCH_OUTPUT` convention of the SIMD assembly benchmark. Off by default: without `IPC_TOOLKIT_WITH_PROFILER` the blocks preprocess away, and the benchmarks are unchanged from before the instrumentation (-5.2% to +1.2% across all four, in both directions, i.e. run-to-run noise). With the profiler on, the CUDA event timers wait on each stage's stop event and serialize stages the build overlaps, costing 3.6% to 27% of the GPU build (most on the smallest scenes) and under 9% everywhere else. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
Adds per-stage profiler coverage to both LBVH broad phases, and a benchmark that dumps the breakdown. Split out of #260, which merged before this commit was pushed.
ipc::LBVH's build was already instrumented;ipc::cuda::LBVHhad none, and neither broad phase separated its detection into traversal and candidate output.ipc::cuda::CudaEventTimer(utils/cuda/cuda_profiler.cuh) is aProfilePointtimer backed by CUDA events, so a device stage is measured by when the device ran it rather than when the host returned from the launch.IPC_TOOLKIT_PROFILE_BLOCK_CUDAis its block macro.ipc::cuda::LBVHgains blocks for the transfers, the box kernels, the domain reduction, and each tree's Morton, sort, hierarchy, and patch stages, plus parent scopes onbuild()anddetect_host()so time not attributed to a stage shows up as a remainder instead of vanishing.materialize()separates the host staging allocation, the device-to-host copy, and theCandidateconstruction.ipc::LBVH::detect_candidates()separates the traversal from the thread-local merge.benchmark_lbvh_stages.cudrives both broad phases over the six benchmark scenes and writes the profiler tree as JSON, following theIPC_TOOLKIT_BENCH_OUTPUTconvention of the SIMD assembly benchmark.What it exposes, on Cloth-Ball (5.2M edge-edge candidates): the device wins the descent outright, 8.19 ms to 2.16 ms (3.8x), then spends 11.74 ms of its 16.96 ms (69%) materializing host
Candidateobjects, against 17.25 ms of 25.46 ms (68%) for the CPU's thread-local merge. The end-to-end detection ratio is set by the candidate output, not by the traversal. On the build side CUB's radix sort is where the win is: 0.54 ms to 0.077 ms per tree, 7.0x.API changes
ipc::cuda::CudaEventTimerandIPC_TOOLKIT_PROFILE_BLOCK_CUDA, both behindIPC_TOOLKIT_WITH_PROFILERand compiled out when it is off.materialize(), wherestd::vector<int32_t> h_a(count), h_b(count)becomes a default construction plusresize(count)so the allocation can be timed; both allocate and zero the same way.Performance
The instrumentation is off by default and free when off. With it on, the CUDA event timers wait on each stage's stop event, serializing stages the build overlaps.
LBVH::buildcuda::LBVH::buildLBVH::detect_edge_edge_candidatescuda::LBVH::detect_edge_edge_candidates((void)0)without Tracy.cudaDeviceSynchronize()at the end of the build. Short kernels cannot hide the fixed sync cost; large ones can.cuda_profiler.cuhsays so at the point of use: take stage shares from a profiling build, absolute times from one without.Type of change
How Has This Been Tested?
[lbvh]and[broad_phase]in CUDA Release with the profiler off: all pass, unchanged from before the instrumentation.[lbvh_stages]in a CUDA Release +IPC_TOOLKIT_WITH_PROFILER=ONbuild, over all six scenes. The benchmark asserts the CPU and GPU return the same candidate count, so the stage split is comparing the same work, and stages are credited self time so they sum to the enclosing call.IPC_TOOLKIT_WITH_PROFILERdiffering (table above).clang-format: 0 replacements.Test Configuration:
-march=native(AVX2+FMA) and-arch=native, ReleaseChecklist