Repository navigation
perf(dropout): replace per-element Philox with one RawKernel - #22
Merged
Merged
Conversation
Both GPU paths ran a full Philox4x32-10 per element, in forward and again in backward, and used one of its four words. SpatialDropout did this for every pixel when only one value per (sample, channel) matters. keep_prob was float64, so every element paid for a double comparison and division, and SpatialDropout added two 64-bit integer operations to recover its index. One RawKernel source, compiled per dtype, replaces the four ElementwiseKernels. Each Philox call now covers 8 elements with 16-bit draws compared against an integer threshold. Dropout stores no mask and rebuilds it in backward. SpatialDropout caches its (S, 1, 1, C) mask. A dropped element is now exactly 0 on both backends, including for NaN and inf inputs, which the NumPy fallback previously turned into NaN.
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.
Context
Both GPU dropout paths ran a full Philox4x32-10 for every element, in forward and again in backward, and used one of its four output words. This replaces the four ElementwiseKernels with one RawKernel source compiled per dtype. It is one commit.
Kernel
Old behavior:
SpatialDropoutran Philox for every pixel, though only one value per (sample, channel) matters. On the 16x16 to 4x4 maps incifar10_breakdown, that is 16-256x redundant.float64 keep_probmade every element pay for a double comparison and division.SpatialDropoutalso did two 64-bit integer operations per element to recover its index.What changed:
element = thread_index * 8 + j).CLAUDE.mdon purpose, so fp16 carries through without extra passes.Dropoutstores no mask. Backward reruns the kernel from the same seed and offset, so there is no O(N) VRAM cost.SpatialDropoutcaches its (S, 1, 1, C) mask, because a rebuild would still need that buffer in each pass.Results on an RX 9070 XT (
cifar10_breakdown, batch 256, 3 repeats):model.train()per stepThat model has no plain
Dropoutlayer. Timed separately, forward only, at rate 0.1:Small tensors see no gain.
Dropped elements are exactly 0
The NumPy fallback multiplied by the mask, so
0 * NaNand0 * infproduced NaN in dropped elements. The fallback now zeroes them first (_apply_mask), matching the GPU kernels and the rule already used inbatchnorm_kernel.py. Kept NaN and inf values still pass through, so real problems stay visible.This is free on GPU, but it makes applying the mask 3-6x slower on NumPy, about 6.7 ms per step on the benchmark's three spatial layers. I kept it so both backends agree.
Behavior changes
TypeErrorfor inputs that aren't fp16, fp32 or fp64.Dropout's GPU kernel. This is harmless because the values compare equal.Layer dtypes under
set_precisionare unchanged frommain, traced per layer on two test models.SpatialDropoutreceives and returns fp32 after a conv becauseConv2dalways outputs fp32.Testing
python3 -m unittest discover tests: 1188 tests pass on NumPy and CuPy (77 skipped), on ROCm in the devcontainer. Ruff is clean.SpatialDropouton GPU and both layers on NumPy. The NumPy failures are the regression frommain; the GPUSpatialDropoutfailure was introduced and fixed within this branch.Not verified:
cuda_fp16.hpath.DropoutbackwardRuntimeErrorhave no test.Follow-ups (out of scope)
make_built_layerpassesseed=only tobuild(), so existing dropout tests that pass it are unseeded and their rate checks could fail by chance. The new tests set_seed_keydirectly.