Repository navigation
feat(hipdnn): add GPU batchnorm backward reference #12520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
BalintCsala
merged 10 commits into
ROCm:develop
from
StreamHPC:users/BalintCsala/batchnorm-backward-gpu-ref
Oct 9, 2026
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
446477c
feat(hipdnn): add GPU batchnorm backward reference
90cc000
PR responses
2cfcd6b
PR comments
5e22fd5
Remove redundant barrier
f0c156f
Merge branch 'develop' into users/BalintCsala/batchnorm-backward-gpu-ref
BalintCsala a78b447
Fix build issue
d1d7327
More change requests
2ea24fc
Merge branch 'develop' into users/BalintCsala/batchnorm-backward-gpu-ref
BalintCsala b060e07
Merge branch 'develop' into users/BalintCsala/batchnorm-backward-gpu-ref
BalintCsala 0a4d94f
Merge branch 'develop' into users/BalintCsala/batchnorm-backward-gpu-ref
BalintCsala File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
153 changes: 153 additions & 0 deletions
153
dnn-providers/integration-tests/gpu-ref/kernels/batchnorm/GpuRefBatchnormBwd.cpp
|
BalintCsala marked this conversation as resolved.
Resolved
|
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| // Copyright © Advanced Micro Devices, Inc., or its affiliates. | ||
| // SPDX-License-Identifier: MIT | ||
|
|
||
| // GPU reference Batchnorm backward kernel. | ||
| // Compiled via HipRTC with all tensor and compute types supplied as preprocessor defines. | ||
| // Each block handles one channel and reduces over its N*spatial elements. It obtains the channel | ||
| // statistics, reduces dscale and dbias, then computes dx from those reduced gradients. | ||
|
|
||
| #include "GpuRefTypes.h" | ||
|
|
||
| using namespace gpu_ref; | ||
|
|
||
| extern "C" __global__ void BatchnormBwdRef(BatchnormBwdArgs args) | ||
| { | ||
| auto* dy = static_cast<const GRAD_OUTPUT_TYPE*>(args.dy); | ||
| auto* input = static_cast<const INPUT_TYPE*>(args.input); | ||
| auto* scale = static_cast<const SCALE_BIAS_TYPE*>(args.scale); | ||
| auto* dx = static_cast<GRAD_INPUT_TYPE*>(args.dx); | ||
| auto* dscale = static_cast<SCALE_BIAS_TYPE*>(args.dscale); | ||
| auto* dbias = static_cast<SCALE_BIAS_TYPE*>(args.dbias); | ||
| auto* savedMean = static_cast<const MEAN_VAR_TYPE*>(args.mean); | ||
| auto* savedInvVariance = static_cast<const MEAN_VAR_TYPE*>(args.invVariance); | ||
|
|
||
| constexpr long long localSize = static_cast<long long>(LOCAL_SIZE); | ||
| constexpr bool isChannelLastLayout = static_cast<bool>(IS_CHANNEL_LAST_LAYOUT); | ||
| const long long channel = static_cast<long long>(blockIdx.x); | ||
| const long long lid = static_cast<long long>(threadIdx.x); | ||
| const long long nhw = args.n * args.hw; | ||
| const long long chw = args.c * args.hw; | ||
| const COMPUTE_TYPE invNhw = static_cast<COMPUTE_TYPE>(1.0) / static_cast<COMPUTE_TYPE>(nhw); | ||
|
|
||
| __shared__ COMPUTE_TYPE reduceA[localSize]; | ||
| __shared__ COMPUTE_TYPE reduceB[localSize]; | ||
|
|
||
| COMPUTE_TYPE channelMean; | ||
| COMPUTE_TYPE channelInvVariance; | ||
| if(savedMean == nullptr || savedInvVariance == nullptr) | ||
| { | ||
| // Compute the channel mean | ||
| COMPUTE_TYPE sum = static_cast<COMPUTE_TYPE>(0); | ||
| for(long long i = lid; i < nhw; i += localSize) | ||
| { | ||
| const long long nidx = i / args.hw; | ||
| const long long hwidx = i - nidx * args.hw; | ||
| const long long index = isChannelLastLayout ? nidx * chw + hwidx * args.c + channel | ||
| : nidx * chw + channel * args.hw + hwidx; | ||
| const COMPUTE_TYPE xValue = toAccum(input[index]); | ||
| sum = sum + xValue; | ||
| } | ||
|
|
||
| reduceA[lid] = sum; | ||
| __syncthreads(); | ||
| for(long long offset = localSize >> 1; offset > 0; offset >>= 1) | ||
| { | ||
| if(lid < offset) | ||
| { | ||
| reduceA[lid] = reduceA[lid] + reduceA[lid + offset]; | ||
| } | ||
| __syncthreads(); | ||
| } | ||
|
|
||
| channelMean = reduceA[0] * invNhw; | ||
|
|
||
| // Compute variance from deviations from the mean to avoid cancellation | ||
| COMPUTE_TYPE varianceSum = static_cast<COMPUTE_TYPE>(0); | ||
| for(long long i = lid; i < nhw; i += localSize) | ||
| { | ||
| const long long nidx = i / args.hw; | ||
| const long long hwidx = i - nidx * args.hw; | ||
| const long long index = isChannelLastLayout ? nidx * chw + hwidx * args.c + channel | ||
| : nidx * chw + channel * args.hw + hwidx; | ||
| const COMPUTE_TYPE deviation = toAccum(input[index]) - channelMean; | ||
| varianceSum = varianceSum + deviation * deviation; | ||
| } | ||
|
|
||
| reduceB[lid] = varianceSum; | ||
| __syncthreads(); | ||
| for(long long offset = localSize >> 1; offset > 0; offset >>= 1) | ||
| { | ||
| if(lid < offset) | ||
| { | ||
| reduceB[lid] = reduceB[lid] + reduceB[lid + offset]; | ||
| } | ||
| __syncthreads(); | ||
| } | ||
|
|
||
| const COMPUTE_TYPE variance = reduceB[0] * invNhw; | ||
| channelInvVariance = rsqrt(variance + toAccum(args.epsilon)); | ||
| } | ||
| else | ||
| { | ||
| channelMean = toAccum(savedMean[channel]); | ||
| channelInvVariance = toAccum(savedInvVariance[channel]); | ||
| } | ||
| __syncthreads(); | ||
|
|
||
| // Accumulate the per-channel scale and bias gradients | ||
| COMPUTE_TYPE dotProduct = static_cast<COMPUTE_TYPE>(0); | ||
| COMPUTE_TYPE sumDy = static_cast<COMPUTE_TYPE>(0); | ||
| for(long long i = lid; i < nhw; i += localSize) | ||
| { | ||
| const long long nidx = i / args.hw; | ||
| const long long hwidx = i - nidx * args.hw; | ||
| const long long index = isChannelLastLayout ? nidx * chw + hwidx * args.c + channel | ||
| : nidx * chw + channel * args.hw + hwidx; | ||
| const COMPUTE_TYPE xValue = toAccum(input[index]); | ||
| const COMPUTE_TYPE dyValue = toAccum(dy[index]); | ||
| const COMPUTE_TYPE xHat = (xValue - channelMean) * channelInvVariance; | ||
| dotProduct = dotProduct + xHat * dyValue; | ||
| sumDy = sumDy + dyValue; | ||
| } | ||
|
|
||
| reduceA[lid] = dotProduct; | ||
|
BalintCsala marked this conversation as resolved.
Resolved
|
||
| reduceB[lid] = sumDy; | ||
| __syncthreads(); | ||
| for(long long offset = localSize >> 1; offset > 0; offset >>= 1) | ||
| { | ||
| if(lid < offset) | ||
| { | ||
| reduceA[lid] = reduceA[lid] + reduceA[lid + offset]; | ||
| reduceB[lid] = reduceB[lid] + reduceB[lid + offset]; | ||
| } | ||
| __syncthreads(); | ||
| } | ||
|
|
||
| const COMPUTE_TYPE channelScale = toAccum(scale[channel]); | ||
| const COMPUTE_TYPE meanDyXhat = reduceA[0] * invNhw; | ||
| const COMPUTE_TYPE meanDy = reduceB[0] * invNhw; | ||
| const COMPUTE_TYPE scalarCoefficient = channelScale * channelInvVariance; | ||
|
|
||
| // Store the reduced affine gradients once per channel | ||
| if(lid == 0) | ||
| { | ||
| SCALE_BIAS_TYPE* tag = nullptr; | ||
| dscale[channel] = fromAccum(reduceA[0], tag); | ||
| dbias[channel] = fromAccum(reduceB[0], tag); | ||
| } | ||
|
|
||
| // Compute the input gradient for each element from the channel reductions | ||
| GRAD_INPUT_TYPE* tag = nullptr; | ||
| for(long long i = lid; i < nhw; i += localSize) | ||
| { | ||
| const long long nidx = i / args.hw; | ||
| const long long hwidx = i - nidx * args.hw; | ||
| const long long index = isChannelLastLayout ? nidx * chw + hwidx * args.c + channel | ||
| : nidx * chw + channel * args.hw + hwidx; | ||
| const COMPUTE_TYPE xValue = toAccum(input[index]); | ||
| const COMPUTE_TYPE dyValue = toAccum(dy[index]); | ||
| const COMPUTE_TYPE xHat = (xValue - channelMean) * channelInvVariance; | ||
| const COMPUTE_TYPE dxValue = (dyValue - meanDy - xHat * meanDyXhat) * scalarCoefficient; | ||
| dx[index] = fromAccum(dxValue, tag); | ||
| } | ||
| } | ||
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
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.