Fix normExp shared-memory reductions and odd-rollout handling - #32
Fix normExp shared-memory reductions and odd-rollout handling#32plancherb1 wants to merge 1 commit into
Conversation
|
Hi Brian, Thank you for the PR! There are definitely some bugs in that section of the code as I haven't looked at it in years. At some point, I tried to do the normalizer and baseline computations on the GPU and found them to be slower than running them on the CPU (at the time anyway) so I never added it to any controller's critical path. And because that code is not used by anything except unit tests, it has been left untouched and buggy. I'm actually surprised that the unit tests have passed on the multiple different computers I have tried them on in the past upon reading your PR. I do have some remarks about the proposed fixes:
As this code is currently not used anywhere in the library but the unit tests, I am curious how you stumbled upon the issue in the first place. Was it just the unit test failing or are you trying to use the If you can get back to me about the result of adding the |
We were trying to use this project and noticed some small bugs that we
wanted to upstream in case others run into similar issues. This change
was AI-assisted, reviewed by the authors, and validated using the tests
reported below.
-Brian
computeBaselineCostandcomputeNormalizerininclude/mppi/core/mppi_common.cuhave two defects that make thesingle-block weight-update path
(
fullGPUcomputeWeights/launchWeightTransformKernel) returnincorrect baselines and normalizers:
__restrict__on the multi-writer shared reduction buffer. Bothhelpers take
reduction_bufferasfloat* __restrict__, but thatbuffer is written by peer threads across
__syncthreads(). Thequalifier is inappropriate for a buffer with cross-thread visibility
requirements. On an RTX 5090 with CUDA 13.2, the restricted version
produces incorrect results and removing the qualifier resolves them:
the existing
NormExpKernel.comparisonTestHostvsDeviceBaselineNormalizerCalculationtest fails on current
main(device baseline is not the true minimumat 10,000 rollouts, data-dependently). A small qualifier matrix
isolates the shared-buffer qualification as the cause —
float* __restrict__fails; plainfloat*,volatile float* __restrict__, andvolatile float*all pass. Weremoved the qualifier rather than adding
volatile:__syncthreads()provides the required block-wide memory ordering,and CUDA's documentation is explicit that
volatileis not asynchronization mechanism.
Odd-rollout leftover handling reads unwritten shared memory, and
races. With odd
num_rollouts, the stage-1 fixup folds inreduction_buffer[num_rollouts - 1], but stage 1 only writesreduction_buffer[0 .. num_rollouts/2), so that slot isuninitialized (the leftover element actually lives in
trajectory_costs_d[num_rollouts - 1]); the fixup also readsreduction_buffer[prev_size - 1]from a different thread with nointervening barrier. Fixed by folding the leftover element into the
first-stage loop, in the same thread that writes
reduction_buffer[prev_size - 1]— no extra barrier needed. Thelater in-loop fixup is correct and unchanged.
Tests:
(below and above the 1024-thread launch). The baseline case places the
true minimum at the last index — the exact element the leftover
handling is responsible for; the normalizer case uses exactly
representable integer values so the serial reference and parallel tree
sum are bit-identical and the exact-equality assert is independent of
reduction order. Both fail on current
main(e.g. baseline 9vs. true minimum 1; sum off by 4x).
fullGPUcomputeWeightsEveryIteration_Testat 10,000rollouts that samples fresh costs and checks every iteration with a
SCOPED_TRACEnaming the failing dataset (the existing comparisontest only asserts the final iteration, and the defect is
data-dependent). On current
mainit fails at iteration 1; with thefix it passes all 500 iterations.
scalar
*output; the store is now gated onthreadIdx.x == 0.Validation on an RTX 5090 (CUDA 13.2): full
normexp_kernel_testspasses 10/10 with the fix, including the previously failing
host-vs-device comparison test;
rmppi_kernel_testspasses 5/5;compute-sanitizer --tool racecheckover all device-touching tests inthe suite reports 0 hazards.