onnxruntime/onnxruntime/test/contrib_ops/layer_norm_test.cc
Weixing Zhang aec4cb489e
ROCm EP for AMD GPU (#5480)
The ROCm EP is designed and implemented based on AMD GPU software stack named ROCm. Here is the link for the details about ROCm: https://rocmdocs.amd.com/en/latest/

ROCm EP was created based on the following things:
1. AMD GPU programming language: HIP
2. AMD GPU HIP language runtime: amdhip64
3. BLAS: rocBLAS, hipBLAS
4. DNN: miOpen
5. Collective Communication library: RCCL
6. cub: hipCub
7. …

Current status:
BERT-L and GPT2 training can be ran on AMD GPU with data parallel.

Next:
1. Make more GPU code be sharable between ROCm EP and CUDA EP since HIP language and HIP runtime API are very close to CUDA.
2. Continue improving the implementation.
3. Continue GPU kernel optimization.
4. Support model parallelism on ROCm EP.
……

The rocm kernels have been removed from this commit and will be in a separate PR. Since the original PR was too big(~180 files), it was suggested to split the PR into two parts, one is rocm-kernels, the other is non rocm kernels.  

Co-authored-by: Weixing Zhang <wezhan@microsoft.com>
Co-authored-by: sabreshao <sabre.shao@amd.com>
Co-authored-by: anghostcici <11013544+anghostcici@users.noreply.github.com>
Co-authored-by: Suffian Khan <sukha@microsoft.com>
Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com>
2020-10-29 17:13:04 -07:00

124 lines
4.6 KiB
C++

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
#include "test/providers/compare_provider_test_utils.h"
namespace onnxruntime {
namespace test {
#if defined(USE_CUDA) || defined(USE_ROCM)
constexpr auto k_epsilon_default = 1e-5f;
constexpr auto k_random_data_min = -10.0f;
constexpr auto k_random_data_max = 10.0f;
const std::string SIMPLIFIED_LAYER_NORM_OP = "SimplifiedLayerNormalization";
const std::string LAYER_NORM_OP = "LayerNormalization";
// The dimensions are split at the specified axis into N (before, exclusive) and M (after, inclusive).
static Status SplitDims(
const std::vector<int64_t>& dims, int64_t axis,
std::vector<int64_t>& n_dims, std::vector<int64_t>& m_dims) {
if (axis < 0) axis += dims.size();
ORT_RETURN_IF_NOT(0 <= axis && static_cast<decltype(dims.size())>(axis) <= dims.size());
const auto boundary = dims.begin() + axis;
n_dims.assign(dims.begin(), boundary);
m_dims.assign(boundary, dims.end());
return Status::OK();
}
static void TestLayerNorm(const std::vector<int64_t>& x_dims,
const std::string& op,
optional<float> epsilon,
int64_t axis = -1,
int64_t keep_dims = 1) {
const std::vector<int64_t>& n_x_m_dims = x_dims;
std::vector<int64_t> n_dims, m_dims;
ASSERT_TRUE(SplitDims(n_x_m_dims, axis, n_dims, m_dims).IsOK());
// n_dims padded with ones
std::vector<int64_t> n_and_ones_dims(n_dims.begin(), n_dims.end());
std::fill_n(std::back_inserter(n_and_ones_dims), n_x_m_dims.size() - n_dims.size(), 1);
// TODO keep_dims is not implemented, default behavior is to keep ones for reduced dimensions
ASSERT_NE(keep_dims, 0);
const std::vector<int64_t>& stats_dims = keep_dims ? n_and_ones_dims : n_dims;
CompareOpTester test(op.c_str());
test.AddAttribute("axis", axis);
test.AddAttribute("keep_dims", keep_dims);
if (epsilon.has_value()) {
test.AddAttribute("epsilon", epsilon.value());
}
// create rand inputs
RandomValueGenerator random{};
std::vector<float> X_data = random.Uniform<float>(n_x_m_dims, k_random_data_min, k_random_data_max);
std::vector<float> scale_data = random.Uniform<float>(m_dims, k_random_data_min, k_random_data_max);
std::vector<float> B_data = random.Uniform<float>(m_dims, k_random_data_min, k_random_data_max);
test.AddInput<float>("X", n_x_m_dims, X_data);
test.AddInput<float>("scale", m_dims, scale_data, true);
if (op.compare(SIMPLIFIED_LAYER_NORM_OP) != 0) {
test.AddInput<float>("B", m_dims, B_data, true);
}
std::vector<float> Y_data = FillZeros<float>(n_x_m_dims);
std::vector<float> mean_data = FillZeros<float>(stats_dims);
std::vector<float> var_data = FillZeros<float>(stats_dims);
test.AddOutput<float>("output", n_x_m_dims, Y_data);
if (op.compare(SIMPLIFIED_LAYER_NORM_OP) != 0) {
test.AddOutput<float>("mean", stats_dims, mean_data);
}
test.AddOutput<float>("var", stats_dims, var_data);
#ifdef USE_CUDA
test.CompareWithCPU(kCudaExecutionProvider);
#elif USE_ROCM
test.CompareWithCPU(kRocmExecutionProvider);
#endif
}
TEST(CudaKernelTest, LayerNorm_SmallSizeTensor) {
const std::vector<int64_t> X_dims{4, 20, 128};
TestLayerNorm(X_dims, LAYER_NORM_OP, k_epsilon_default);
}
TEST(CudaKernelTest, LayerNorm_SmallSizeTensor_IntermediateAxis) {
const std::vector<int64_t> X_dims{4, 20, 8, 16};
const int64_t axis = -2;
TestLayerNorm(X_dims, LAYER_NORM_OP, k_epsilon_default, axis);
}
TEST(CudaKernelTest, LayerNorm_MidSizeTensor) {
std::vector<int64_t> X_dims{8, 80, 768};
TestLayerNorm(X_dims, LAYER_NORM_OP, k_epsilon_default);
}
TEST(CudaKernelTest, LayerNorm_LargeSizeTensor) {
std::vector<int64_t> X_dims{16, 512, 1024};
TestLayerNorm(X_dims, LAYER_NORM_OP, k_epsilon_default);
}
TEST(CudaKernelTest, SimplifiedLayerNorm_SmallSizeTensor) {
const std::vector<int64_t> X_dims{4, 20, 128};
TestLayerNorm(X_dims, SIMPLIFIED_LAYER_NORM_OP, k_epsilon_default);
}
TEST(CudaKernelTest, SimplifiedLayerNorm_SmallSizeTensor_IntermediateAxis) {
const std::vector<int64_t> X_dims{4, 20, 8, 16};
const int64_t axis = -2;
TestLayerNorm(X_dims, SIMPLIFIED_LAYER_NORM_OP, k_epsilon_default, axis);
}
TEST(CudaKernelTest, SimplifiedLayerNorm_MidSizeTensor) {
std::vector<int64_t> X_dims{8, 80, 768};
TestLayerNorm(X_dims, SIMPLIFIED_LAYER_NORM_OP, k_epsilon_default);
}
TEST(CudaKernelTest, SimplifiedLayerNorm_LargeSizeTensor) {
std::vector<int64_t> X_dims{16, 512, 1024};
TestLayerNorm(X_dims, SIMPLIFIED_LAYER_NORM_OP, k_epsilon_default);
}
#endif
} // namespace test
} // namespace onnxruntime