Skip to content

Commit 7e8fc29

Browse files
authored
[CUDA] Deprecate SkipLayerNorm strict mode (#29388)
### Description Removes the deprecated `enable_skip_layer_norm_strict_mode` CUDA provider option and its associated "strict mode" code path from the CUDA `SkipLayerNorm` kernel. Strict mode previously routed `SkipLayerNorm` through the `LayerNormalization` kernel to gain fp32-accumulation accuracy at the cost of performance. After #28682 made `SkipLayerNorm`/`EmbedLayerNorm` CUDA kernels always accumulate in fp32, the strict-mode path is redundant: the default kernel already provides the same accuracy with better performance. This change deletes the now-dead branch and the plumbing that fed it. ### Key Changes - **`skip_layer_norm.cc`**: Remove the `strict_` branch that called `HostApplyLayerNorm`; always launch `LaunchSkipLayerNormKernel`. Drop the now-unused `layer_norm_impl.h` include and the input/skip same-shape strict-mode shape check. - **`skip_layer_norm.h`**: Remove the `strict_` member. - **`cuda_execution_provider.h`**: Remove `IsSkipLayerNormInStrictMode()`. - **`cuda_kernel_adapter.h`**: Remove `GetCudaKernelAdapterSkipLayerNormStrictMode()` shim. - **`cuda_provider_options.h`**: Keep `enable_skip_layer_norm_strict_mode` field for ABI/back-compat but mark it deprecated and ignored. - **`skiplayernorm_op_test.cc`**: Drop the redundant strict-mode test passes; tests now run a single default path. The provider option is retained (ignored) to preserve backward compatibility — existing configs that set it continue to work without error. ### Motivation Follow-up cleanup to #28682, which switched the CUDA kernels to fp32 accumulation, making strict mode obsolete. ### Testing - `skiplayernorm_op_test.cc` covers fp16/fp32/bf16 default path; strict-mode passes removed.
1 parent c47718e commit 7e8fc29

13 files changed

Lines changed: 63 additions & 126 deletions

File tree

include/onnxruntime/core/providers/cuda/cuda_provider_options.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ struct OrtCUDAProviderOptionsV2 {
3333
int tunable_op_enable = 0; // flag specifying if TunableOp is enabled.
3434
int tunable_op_tuning_enable = 0; // flag specifying if TunableOp is enabled for tuning, this relies on TunableOp is enabled.
3535
int tunable_op_max_tuning_duration_ms = 0; // Max tuning duration time limit for TunableOp.
36-
int enable_skip_layer_norm_strict_mode = 0; // flag specifying if SkipLayerNorm is in strict mode. If true, use LayerNormalization kernel.
37-
// The strict mode has better accuracy but lower performance.
36+
int enable_skip_layer_norm_strict_mode = 0; // [Deprecated] Accepted for ABI/back-compat but not stored in EP info. SkipLayerNorm always accumulates in fp32.
37+
// Setting it has no effect on computation or output.
3838
int prefer_nhwc = 0; // make the CUDA EP NHWC preferred
3939
int use_ep_level_unified_stream = 0; // flag specifying if ep level stream is used or not
4040
int use_tf32 = 1; // use TF32

onnxruntime/contrib_ops/cuda/bert/skip_layer_norm.cc

Lines changed: 28 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the MIT License.
33

44
#include "core/providers/cuda/cuda_common.h"
5-
#include "core/providers/cuda/nn/layer_norm_impl.h"
65
#include "core/common/narrow.h"
76
#include "skip_layer_norm.h"
87
#include "skip_layer_norm_impl.h"
@@ -42,26 +41,14 @@ template <typename T, bool Simplified>
4241
SkipLayerNorm<T, Simplified>::SkipLayerNorm(const OpKernelInfo& op_kernel_info) : CudaKernel(op_kernel_info) {
4342
ORT_ENFORCE(op_kernel_info.GetAttr<float>("epsilon", &epsilon_).IsOK());
4443
ORT_ENFORCE(epsilon_ >= 0);
45-
46-
#ifdef BUILD_CUDA_EP_AS_PLUGIN
47-
// Plugin adapter cannot static_cast to CUDAExecutionProvider directly.
48-
// Use the adapter shim that reads the config from the per-EP runtime map.
49-
strict_ = onnxruntime::cuda::GetCudaKernelAdapterSkipLayerNormStrictMode(op_kernel_info.GetExecutionProvider());
50-
#else
51-
const CUDAExecutionProvider* cuda_ep = static_cast<const CUDAExecutionProvider*>(op_kernel_info.GetExecutionProvider());
52-
strict_ = cuda_ep->IsSkipLayerNormInStrictMode();
53-
#endif
44+
// Note: the enable_skip_layer_norm_strict_mode provider option is deprecated and ignored.
45+
// The kernel always accumulates in fp32, so the previous strict-mode path is no longer needed.
5446
}
5547

5648
template <typename T, bool Simplified>
5749
Status SkipLayerNorm<T, Simplified>::ComputeInternal(OpKernelContext* ctx) const {
5850
const Tensor* input = ctx->Input<Tensor>(0);
5951
const Tensor* skip = ctx->Input<Tensor>(1);
60-
if (strict_ && skip->Shape() != input->Shape()) {
61-
return ORT_MAKE_STATUS(ONNXRUNTIME, INVALID_ARGUMENT,
62-
"'input' and 'skip' shall have same shape when enable_skip_layer_norm_strict_mode is True");
63-
}
64-
6552
const Tensor* gamma = ctx->Input<Tensor>(2);
6653

6754
const Tensor* beta = Simplified ? nullptr : ctx->Input<Tensor>(3);
@@ -94,53 +81,34 @@ Status SkipLayerNorm<T, Simplified>::ComputeInternal(OpKernelContext* ctx) const
9481

9582
const int skip_size = onnxruntime::narrow<int>(skip->Shape().Size());
9683

97-
if (strict_) {
98-
HostApplyLayerNorm<CudaT, float, CudaT, Simplified>(
99-
GetDeviceProp(),
84+
if constexpr (std::is_same_v<T, BFloat16>) {
85+
LaunchSkipLayerNormKernel<nv_bfloat16, Simplified>(
10086
Stream(ctx),
101-
reinterpret_cast<CudaT*>(output->MutableData<T>()), // Y_data
102-
nullptr, // mean_data
103-
nullptr, // inv_var_data
104-
reinterpret_cast<const CudaT*>(input->Data<T>()), // X_data
105-
row_count, // n1
106-
hidden_size, // n2
107-
(double)epsilon_, // epsilon
108-
reinterpret_cast<const CudaT*>(gamma->Data<T>()), // gamma
109-
(beta != nullptr) ? reinterpret_cast<const CudaT*>(beta->Data<T>()) : nullptr, // beta
110-
0, // no broadcast for gamma/beta
111-
reinterpret_cast<const CudaT*>(skip->Data<T>()), // skip or residual to add
112-
(bias != nullptr) ? reinterpret_cast<const CudaT*>(bias->Data<T>()) : nullptr, // bias to add
113-
sum_output != nullptr ? reinterpret_cast<CudaT*>(sum_output->MutableData<T>()) : nullptr);
87+
reinterpret_cast<nv_bfloat16*>(output->MutableData<T>()),
88+
sum_output != nullptr ? reinterpret_cast<nv_bfloat16*>(sum_output->MutableData<T>()) : nullptr,
89+
reinterpret_cast<const nv_bfloat16*>(input->Data<T>()),
90+
reinterpret_cast<const nv_bfloat16*>(skip->Data<T>()),
91+
(bias != nullptr) ? reinterpret_cast<const nv_bfloat16*>(bias->Data<T>()) : nullptr,
92+
reinterpret_cast<const nv_bfloat16*>(gamma->Data<T>()),
93+
(beta != nullptr) ? reinterpret_cast<const nv_bfloat16*>(beta->Data<T>()) : nullptr,
94+
epsilon_,
95+
hidden_size,
96+
row_count,
97+
skip_size);
11498
} else {
115-
if constexpr (std::is_same_v<T, BFloat16>) {
116-
LaunchSkipLayerNormKernel<nv_bfloat16, Simplified>(
117-
Stream(ctx),
118-
reinterpret_cast<nv_bfloat16*>(output->MutableData<T>()),
119-
sum_output != nullptr ? reinterpret_cast<nv_bfloat16*>(sum_output->MutableData<T>()) : nullptr,
120-
reinterpret_cast<const nv_bfloat16*>(input->Data<T>()),
121-
reinterpret_cast<const nv_bfloat16*>(skip->Data<T>()),
122-
(bias != nullptr) ? reinterpret_cast<const nv_bfloat16*>(bias->Data<T>()) : nullptr,
123-
reinterpret_cast<const nv_bfloat16*>(gamma->Data<T>()),
124-
(beta != nullptr) ? reinterpret_cast<const nv_bfloat16*>(beta->Data<T>()) : nullptr,
125-
epsilon_,
126-
hidden_size,
127-
row_count,
128-
skip_size);
129-
} else {
130-
LaunchSkipLayerNormKernel<CudaT, Simplified>(
131-
Stream(ctx),
132-
reinterpret_cast<CudaT*>(output->MutableData<T>()),
133-
sum_output != nullptr ? reinterpret_cast<CudaT*>(sum_output->MutableData<T>()) : nullptr,
134-
reinterpret_cast<const CudaT*>(input->Data<T>()),
135-
reinterpret_cast<const CudaT*>(skip->Data<T>()),
136-
(bias != nullptr) ? reinterpret_cast<const CudaT*>(bias->Data<T>()) : nullptr,
137-
reinterpret_cast<const CudaT*>(gamma->Data<T>()),
138-
(beta != nullptr) ? reinterpret_cast<const CudaT*>(beta->Data<T>()) : nullptr,
139-
epsilon_,
140-
hidden_size,
141-
row_count,
142-
skip_size);
143-
}
99+
LaunchSkipLayerNormKernel<CudaT, Simplified>(
100+
Stream(ctx),
101+
reinterpret_cast<CudaT*>(output->MutableData<T>()),
102+
sum_output != nullptr ? reinterpret_cast<CudaT*>(sum_output->MutableData<T>()) : nullptr,
103+
reinterpret_cast<const CudaT*>(input->Data<T>()),
104+
reinterpret_cast<const CudaT*>(skip->Data<T>()),
105+
(bias != nullptr) ? reinterpret_cast<const CudaT*>(bias->Data<T>()) : nullptr,
106+
reinterpret_cast<const CudaT*>(gamma->Data<T>()),
107+
(beta != nullptr) ? reinterpret_cast<const CudaT*>(beta->Data<T>()) : nullptr,
108+
epsilon_,
109+
hidden_size,
110+
row_count,
111+
skip_size);
144112
}
145113

146114
CUDA_RETURN_IF_ERROR(cudaGetLastError());

onnxruntime/contrib_ops/cuda/bert/skip_layer_norm.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ class SkipLayerNorm final : public CudaKernel {
1919

2020
private:
2121
float epsilon_;
22-
bool strict_;
2322
};
2423

2524
} // namespace cuda

onnxruntime/core/providers/cuda/cuda_execution_provider.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ class CUDAExecutionProvider : public IExecutionProvider {
8383
bool DoCopyOnDefaultStream() const { return info_.do_copy_in_default_stream; }
8484
bool GetCudnnConvUseMaxWorkspace() const { return info_.cudnn_conv_use_max_workspace; }
8585
bool GetCudnnConv1dPadToNc1d() const { return info_.cudnn_conv1d_pad_to_nc1d; }
86-
bool IsSkipLayerNormInStrictMode() const { return info_.enable_skip_layer_norm_strict_mode; }
8786
bool IsNHWCPreferred() const { return info_.prefer_nhwc; }
8887
bool IsFuseConvBias() const { return info_.fuse_conv_bias; }
8988
bool UseTF32() const { return info_.use_tf32; }

onnxruntime/core/providers/cuda/cuda_execution_provider_info.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ constexpr const char* kCudnnConv1dPadToNc1d = "cudnn_conv1d_pad_to_nc1d";
3030
constexpr const char* kTunableOpEnable = "tunable_op_enable";
3131
constexpr const char* kTunableOpTuningEnable = "tunable_op_tuning_enable";
3232
constexpr const char* kTunableOpMaxTuningDurationMs = "tunable_op_max_tuning_duration_ms";
33+
// [Deprecated] Accepted but ignored: SkipLayerNorm always accumulates in fp32.
3334
constexpr const char* kEnableSkipLayerNormStrictMode = "enable_skip_layer_norm_strict_mode";
3435
constexpr const char* kPreferNHWCMode = "prefer_nhwc";
3536
constexpr const char* kUseEPLevelUnifiedStream = "use_ep_level_unified_stream";
@@ -115,7 +116,15 @@ CUDAExecutionProviderInfo CUDAExecutionProviderInfo::FromProviderOptions(const P
115116
.AddAssignmentToReference(cuda::provider_option_names::kCudnnConvUseMaxWorkspace, info.cudnn_conv_use_max_workspace)
116117
.AddAssignmentToReference(cuda::provider_option_names::kEnableCudaGraph, info.enable_cuda_graph)
117118
.AddAssignmentToReference(cuda::provider_option_names::kCudnnConv1dPadToNc1d, info.cudnn_conv1d_pad_to_nc1d)
118-
.AddAssignmentToReference(cuda::provider_option_names::kEnableSkipLayerNormStrictMode, info.enable_skip_layer_norm_strict_mode)
119+
.AddValueParser(
120+
cuda::provider_option_names::kEnableSkipLayerNormStrictMode,
121+
[](const std::string& value_str) -> Status {
122+
// [Deprecated] Accept the option for backward compatibility, but do not store it:
123+
// SkipLayerNorm always accumulates in fp32, so strict mode has no effect.
124+
bool ignored = false;
125+
ORT_RETURN_IF_ERROR(ParseStringWithClassicLocale(value_str, ignored));
126+
return Status::OK();
127+
})
119128
.AddAssignmentToReference(cuda::provider_option_names::kPreferNHWCMode, info.prefer_nhwc)
120129
.AddAssignmentToReference(cuda::provider_option_names::kUseEPLevelUnifiedStream, info.use_ep_level_unified_stream)
121130
.AddAssignmentToReference(cuda::provider_option_names::kUseTF32, info.use_tf32)
@@ -170,7 +179,6 @@ ProviderOptions CUDAExecutionProviderInfo::ToProviderOptions(const CUDAExecution
170179
{cuda::provider_option_names::kTunableOpEnable, MakeStringWithClassicLocale(info.tunable_op.enable)},
171180
{cuda::provider_option_names::kTunableOpTuningEnable, MakeStringWithClassicLocale(info.tunable_op.tuning_enable)},
172181
{cuda::provider_option_names::kTunableOpMaxTuningDurationMs, MakeStringWithClassicLocale(info.tunable_op.max_tuning_duration_ms)},
173-
{cuda::provider_option_names::kEnableSkipLayerNormStrictMode, MakeStringWithClassicLocale(info.enable_skip_layer_norm_strict_mode)},
174182
{cuda::provider_option_names::kPreferNHWCMode, MakeStringWithClassicLocale(info.prefer_nhwc)},
175183
{cuda::provider_option_names::kUseEPLevelUnifiedStream, MakeStringWithClassicLocale(info.use_ep_level_unified_stream)},
176184
{cuda::provider_option_names::kUseTF32, MakeStringWithClassicLocale(info.use_tf32)},

onnxruntime/core/providers/cuda/cuda_execution_provider_info.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ struct CUDAExecutionProviderInfo {
7171

7272
cuda::TunableOpInfo tunable_op{};
7373

74-
bool enable_skip_layer_norm_strict_mode{false};
7574
bool prefer_nhwc{false};
7675

7776
bool use_ep_level_unified_stream{false};
@@ -105,7 +104,6 @@ struct std::hash<::onnxruntime::CUDAExecutionProviderInfo> {
105104
(static_cast<size_t>(info.tunable_op.enable) << 24) ^
106105
(static_cast<size_t>(info.tunable_op.tuning_enable) << 25) ^
107106
(static_cast<size_t>(info.cudnn_conv1d_pad_to_nc1d) << 26) ^
108-
(static_cast<size_t>(info.enable_skip_layer_norm_strict_mode) << 27) ^
109107
(static_cast<size_t>(info.prefer_nhwc) << 28) ^
110108
(static_cast<size_t>(info.use_ep_level_unified_stream) << 29) ^
111109
(static_cast<size_t>(info.use_tf32) << 30) ^

onnxruntime/core/providers/cuda/cuda_provider_factory.cc

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,6 @@ struct CUDA_Provider : Provider {
242242
info.tunable_op.enable = params->tunable_op_enable;
243243
info.tunable_op.tuning_enable = params->tunable_op_tuning_enable;
244244
info.tunable_op.max_tuning_duration_ms = params->tunable_op_max_tuning_duration_ms;
245-
info.enable_skip_layer_norm_strict_mode = params->enable_skip_layer_norm_strict_mode != 0;
246245
info.use_ep_level_unified_stream = params->use_ep_level_unified_stream != 0;
247246
info.use_tf32 = params->use_tf32 != 0;
248247
info.sdpa_kernel = params->sdpa_kernel;
@@ -276,7 +275,6 @@ struct CUDA_Provider : Provider {
276275
cuda_options.cudnn_conv_use_max_workspace = internal_options.cudnn_conv_use_max_workspace;
277276
cuda_options.enable_cuda_graph = internal_options.enable_cuda_graph;
278277
cuda_options.cudnn_conv1d_pad_to_nc1d = internal_options.cudnn_conv1d_pad_to_nc1d;
279-
cuda_options.enable_skip_layer_norm_strict_mode = internal_options.enable_skip_layer_norm_strict_mode;
280278
cuda_options.prefer_nhwc = internal_options.prefer_nhwc;
281279
cuda_options.use_ep_level_unified_stream = internal_options.use_ep_level_unified_stream;
282280
cuda_options.use_tf32 = internal_options.use_tf32;

onnxruntime/core/providers/cuda/cuda_stream_handle.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,9 @@ void* CudaStream::GetResource(int version, int id) const {
214214
return reinterpret_cast<void*>(ep_info_.cudnn_conv1d_pad_to_nc1d);
215215
break;
216216
case CudaResource::enable_skip_layer_norm_strict_mode_t:
217-
return reinterpret_cast<void*>(ep_info_.enable_skip_layer_norm_strict_mode);
217+
// [Deprecated] SkipLayerNorm always accumulates in fp32; the strict-mode option no longer
218+
// affects computation. Kept for backward compatibility and always reported as false.
219+
return reinterpret_cast<void*>(false);
218220
break;
219221
case CudaResource::prefer_nhwc_t:
220222
return reinterpret_cast<void*>(ep_info_.prefer_nhwc);

onnxruntime/core/providers/cuda/plugin/cuda_ep.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ CudaEp::CudaEp(CudaEpFactory& factory, const Config& config, const OrtLogger& lo
202202
// below — no function-signature change.
203203
onnxruntime::cuda::detail::CudaKernelAdapterRuntimeConfig adapter_config;
204204
adapter_config.use_tf32 = config_.use_tf32;
205-
adapter_config.skip_layer_norm_strict_mode = config_.enable_skip_layer_norm_strict_mode;
206205
adapter_config.cudnn_conv_algo = config_.cudnn_conv_algo;
207206
adapter_config.cudnn_conv_use_max_workspace = config_.cudnn_conv_use_max_workspace;
208207
adapter_config.cudnn_conv1d_pad_to_nc1d = config_.cudnn_conv1d_pad_to_nc1d;

onnxruntime/core/providers/cuda/plugin/cuda_ep.h

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,24 +24,23 @@ class CudaEp : public onnxruntime::ep::adapter::Ep {
2424
public:
2525
/// Configuration parameters for the CUDA EP, parsed from session options.
2626
struct Config {
27-
bool prefer_nhwc = false; ///< Use NHWC data layout when available.
28-
bool use_tf32 = true; ///< Enable TF32 math on Ampere+ GPUs.
29-
bool enable_skip_layer_norm_strict_mode = false; ///< Strict mode for SkipLayerNorm kernel.
30-
int device_id = 0; ///< CUDA device ordinal.
31-
int cudnn_conv_algo = 0; ///< cuDNN convolution algorithm selection.
32-
bool cudnn_conv_use_max_workspace = true; ///< Use maximum workspace for cuDNN conv algo search.
33-
bool cudnn_conv1d_pad_to_nc1d = false; ///< Pad 1D convolutions to NC1D format.
34-
bool fuse_conv_bias = false; ///< Enable cuDNN frontend conv+bias fusion.
35-
int sdpa_kernel = 0; ///< Attention backend bitmask override.
36-
bool enable_cuda_graph = false; ///< Enable CUDA graph capture and replay.
37-
int min_num_runs_before_cuda_graph_capture = 2; ///< Warm-up runs before graph capture begins.
38-
bool has_user_compute_stream = false; ///< Whether user provided an external CUDA stream.
39-
void* user_compute_stream = nullptr; ///< User-provided CUDA stream (cudaStream_t cast to void*).
40-
bool do_copy_in_default_stream = true; ///< Use default stream for H2D/D2H copies.
41-
bool use_ep_level_unified_stream = false; ///< Force all ops to share one stream (no concurrency).
42-
void* external_alloc = nullptr; ///< External GPU memory allocation function pointer.
43-
void* external_free = nullptr; ///< External GPU memory deallocation function pointer.
44-
void* external_empty_cache = nullptr; ///< External GPU memory cache-clear function pointer.
27+
bool prefer_nhwc = false; ///< Use NHWC data layout when available.
28+
bool use_tf32 = true; ///< Enable TF32 math on Ampere+ GPUs.
29+
int device_id = 0; ///< CUDA device ordinal.
30+
int cudnn_conv_algo = 0; ///< cuDNN convolution algorithm selection.
31+
bool cudnn_conv_use_max_workspace = true; ///< Use maximum workspace for cuDNN conv algo search.
32+
bool cudnn_conv1d_pad_to_nc1d = false; ///< Pad 1D convolutions to NC1D format.
33+
bool fuse_conv_bias = false; ///< Enable cuDNN frontend conv+bias fusion.
34+
int sdpa_kernel = 0; ///< Attention backend bitmask override.
35+
bool enable_cuda_graph = false; ///< Enable CUDA graph capture and replay.
36+
int min_num_runs_before_cuda_graph_capture = 2; ///< Warm-up runs before graph capture begins.
37+
bool has_user_compute_stream = false; ///< Whether user provided an external CUDA stream.
38+
void* user_compute_stream = nullptr; ///< User-provided CUDA stream (cudaStream_t cast to void*).
39+
bool do_copy_in_default_stream = true; ///< Use default stream for H2D/D2H copies.
40+
bool use_ep_level_unified_stream = false; ///< Force all ops to share one stream (no concurrency).
41+
void* external_alloc = nullptr; ///< External GPU memory allocation function pointer.
42+
void* external_free = nullptr; ///< External GPU memory deallocation function pointer.
43+
void* external_empty_cache = nullptr; ///< External GPU memory cache-clear function pointer.
4544
};
4645

4746
CudaEp(CudaEpFactory& factory, const Config& config, const OrtLogger& logger);

0 commit comments

Comments
 (0)