// Copyright © Advanced Micro Devices, Inc., or its affiliates. // SPDX-License-Identifier: MIT #include #include #include #include "../cpu_conv.hpp" #include "../random.hpp" #include "../tensor_holder.hpp" #include "../verify.hpp" namespace { struct TestCase2D { int n; int wi; int hi; int c; int k; int fx; int fy; int px; int py; int sx; int sy; int dx; int dy; int g; }; struct TestCase3D { int n; int di; int wi; int hi; int c; int k; int fz; int fx; int fy; int pz; int px; int py; int sz; int sx; int sy; int dz; int dx; int dy; int g; }; static constexpr int RAND_INTEGER_MAX = 5; static constexpr int RAND_INTEGER_MIN = -4; static constexpr float MAX_INTEGER_INTERVAL = 4.f; auto NameGenerator(const ::testing::TestParamInfo& info) { std::stringstream ss{}; ss << "n" << info.param.n << "wi" << info.param.wi << "hi" << info.param.hi << "c" << info.param.c << "k" << info.param.k << "fx" << info.param.fx << "fy" << info.param.fy << "px" << info.param.px << "py" << info.param.py << "sx" << info.param.sx << "sy" << info.param.sy << "dx" << info.param.dx << "dy" << info.param.dy << "g" << info.param.g; return ss.str(); } auto NameGenerator(const ::testing::TestParamInfo& info) { std::stringstream ss{}; ss << "n" << info.param.n << "di" << info.param.di << "wi" << info.param.wi << "hi" << info.param.hi << "c" << info.param.c << "k" << info.param.k << "fz" << info.param.fz << "fx" << info.param.fx << "fy" << info.param.fy << "pz" << info.param.pz << "px" << info.param.px << "py" << info.param.py << "sz" << info.param.sz << "sx" << info.param.sx << "sy" << info.param.sy << "dz" << info.param.dz << "dx" << info.param.dx << "dy" << info.param.dy << "g" << info.param.g; return ss.str(); } int conv_out_size(int in_size, int pad, int dilation, int ksize, int stride) { return (in_size + 2 * pad - dilation * (ksize - 1) - 1) / stride + 1; } /* * for half, if we use integer, half can express -2048 ~ 2048 without data-loss. * e.g. 2049 can not expressed by half. * from 2048~4096, half can only express 1/2 the number. number 2049, 2051, 2053, 2055.... can not * be expressed. (max interval is 2) from 4096~8192, half can only express 1/4 the number. number * 4097, 4098, 4099, 4101, 4102, 4103, 4105, 4106, 4107, 4109... can not expressd. (max interval is * 4) from 8192~16384, half can only express 1/8 the number. (max interval is 8) */ template void rand_tensor_integer(tensor& t, int max = RAND_INTEGER_MAX, int min = RAND_INTEGER_MIN) { // use integer to random. for(size_t i = 0; i < t.data.size(); i++) t[i] = static_cast(prng::gen_A_to_B(min, max)); } std::vector get_image_depth() { return {8, 10}; } std::vector get_image_size() { return {9, 14}; } // Warning: Channel size must be multiple of group size std::vector get_channel_size() { return {4, 8}; } std::vector get_filter_depth() { return {1, 3}; } std::vector get_filter_size() { return {1, 3}; } std::vector get_stride_depth() { return {1, 2}; } std::vector get_dilation_depth() { return {1}; } std::vector get_stride_dilation_size() { return {1, 2}; } std::vector get_pad_depth() { return {0, 1}; } std::vector get_pad_size() { return {0, 1}; } std::vector get_group_size() { return {1, 2}; } std::vector get_batch_size() { return {1, 2}; } std::vector generate_conv_2d() { std::vector result{}; for(int c : get_channel_size()) { for(int hi : get_image_size()) { for(int wi : get_image_size()) { for(int fy : get_filter_size()) { for(int fx : get_filter_size()) { for(int py : get_pad_size()) { for(int px : get_pad_size()) { int n = get_batch_size()[prng::gen_canonical()]; int g = get_group_size()[prng::gen_canonical()]; int k = get_channel_size()[prng::gen_canonical()]; int sy = get_stride_dilation_size()[prng::gen_canonical()]; int sx = get_stride_dilation_size()[prng::gen_canonical()]; int dy = get_stride_dilation_size()[prng::gen_canonical()]; int dx = get_stride_dilation_size()[prng::gen_canonical()]; int ho = conv_out_size(hi, py, dy, fy, sy); int wo = conv_out_size(wi, px, dx, fx, sx); if(fy > hi || fx > wi || (fy - 1) < py || (fx - 1) < px || ho <= 0 || wo <= 0 || c % g != 0 || k % g != 0) continue; if((fx == 3 && fy == 5) || (fx == 5 && fy == 3)) continue; result.push_back( TestCase2D{n, wi, hi, c, k, fx, fy, px, py, sx, sy, dx, dy, g}); } } } } } } } return result; } auto GenCases2D() { static const auto cases = generate_conv_2d(); return cases; } std::vector generate_conv_3d() { std::vector result{}; for(int c : get_channel_size()) { for(int fy : get_filter_size()) { for(int fx : get_filter_size()) { for(int py : get_pad_size()) { for(int px : get_pad_size()) { int n = get_batch_size()[prng::gen_canonical()]; int g = get_group_size()[prng::gen_canonical()]; int k = get_channel_size()[prng::gen_canonical()]; int di = get_image_depth()[prng::gen_canonical()]; int hi = get_image_size()[prng::gen_canonical()]; int wi = get_image_size()[prng::gen_canonical()]; int fz = get_filter_depth()[prng::gen_canonical()]; int pz = get_pad_depth()[prng::gen_canonical()]; int sx = get_stride_dilation_size()[prng::gen_canonical()]; int sy = get_stride_dilation_size()[prng::gen_canonical()]; int sz = get_stride_depth()[prng::gen_canonical()]; int dx = get_stride_dilation_size()[prng::gen_canonical()]; int dy = get_stride_dilation_size()[prng::gen_canonical()]; int dz = get_dilation_depth()[0]; int ho = conv_out_size(hi, py, dy, fy, sy); int wo = conv_out_size(wi, px, dx, fx, sx); int do_ = conv_out_size(di, pz, dz, fz, sz); if(fy > hi || fx > wi || fz > di || (fy - 1) < py || (fx - 1) < px || (fz - 1) < pz || ho <= 0 || wo <= 0 || do_ <= 0 || c % g != 0 || k % g != 0) continue; if((fx == 3 && fy == 5) || (fx == 5 && fy == 3)) continue; result.push_back(TestCase3D{n, di, wi, hi, c, k, fz, fx, fy, pz, px, py, sz, sx, sy, dz, dx, dy, g}); } } } } } return result; } auto GenCases3D() { static const auto cases = generate_conv_3d(); return cases; } template bool verify_tensor(tensor& t_gpu, tensor& t_cpu, float integer_interval = MAX_INTEGER_INTERVAL) { EXPECT_EQ(t_gpu.data.size(), t_cpu.data.size()) << "size not equal, should not happen\n"; auto idx = miopen::mismatch_idx(t_gpu.data, t_cpu.data, miopen::float_equal); bool valid_result = true; if(idx < miopen::range_distance(t_cpu)) { // give a re-try chance for half_float // max gemm_k is wrw, max_n=2, max_ho/wo=14, max integer=4, max value=2*14*14*4*4 = 6272. // hence max integer interval is 4 // for gpu we cast value to float, for cpu we cast value to double. hence inside kernel // precision is guaranteed. // the problem is cast the result back. // round-to-nearest(default rounding mode) seems will have little difference when doing // double->half, compare to float->half. // hence we give a chance to calculate if the difference is still following our experience, // while doing integer computation. auto max_diff = miopen::max_diff(t_gpu, t_cpu); if(max_diff > integer_interval) valid_result = false; } EXPECT_TRUE(valid_result) << "diff at:" << idx << ", gpu:" << t_gpu[idx] << ", cpu:" << t_cpu[idx] << std::endl; return valid_result; } /// input: a vector of lengths of dims in a tensor /// multiply each element with a random constant integer void pad_tensor_strides(std::vector& strides) { constexpr int min_stride_multiplier = 1; constexpr int max_stride_multiplier = 5; auto c = prng::gen_A_to_B(min_stride_multiplier, max_stride_multiplier); for(auto& v : strides) { // cppcheck-suppress useStlAlgorithm v = v * c; } } std::string direction_to_string(miopen::conv::Direction direction) { if(direction == miopen::conv::Direction::Forward) return "fwd"; if(direction == miopen::conv::Direction::BackwardData) return "bwd"; if(direction == miopen::conv::Direction::BackwardWeights) return "wrw"; return "n/a"; } std::string miopen_type_to_string(miopenDataType_t type) { if(type == miopenHalf) return "fp16"; if(type == miopenFloat) return "fp32"; if(type == miopenInt32) return "int32"; if(type == miopenInt8) return "int8"; if(type == miopenBFloat16) return "bf16"; return "n/a"; } template struct gpu_reference_conv_2d : public ::testing::TestWithParam { miopenHandle_t handle{}; void SetUp() override { prng::reset_seed(); miopenCreate(&handle); } void TearDown() override { miopenDestroy(handle); } void Run() { auto const& param = GetParam(); miopenConvolutionDescriptor_t convDesc; miopenTensorDescriptor_t inDesc, weiDesc, outDesc; int pads[] = {param.py, param.px}; int strides[] = {param.sy, param.sx}; int dilations[] = {param.dy, param.dx}; int ho = conv_out_size(param.hi, param.py, param.dy, param.fy, param.sy); int wo = conv_out_size(param.wi, param.px, param.dx, param.fx, param.sx); int c_per_group = param.c / param.g; std::vector in_len({param.n, param.c, param.hi, param.wi}); std::vector wei_len({param.k, c_per_group, param.fy, param.fx}); std::vector out_len({param.n, param.k, ho, wo}); std::vector in_strides; std::vector wei_strides; std::vector out_strides; std::string layout_default = miopen::tensor_layout_get_default(4); std::string layout_string = miopen::TensorDescriptor::LayoutEnumToStr(tensor_layout); miopen::tensor_layout_to_strides(in_len, layout_default, layout_string, in_strides); miopen::tensor_layout_to_strides(wei_len, layout_default, layout_string, wei_strides); miopen::tensor_layout_to_strides(out_len, layout_default, layout_string, out_strides); pad_tensor_strides(in_strides); pad_tensor_strides(wei_strides); pad_tensor_strides(out_strides); tensor in(in_len, in_strides); tensor wei(wei_len, wei_strides); tensor out(out_len, out_strides); auto in_sz = in.data.size(); auto wei_sz = wei.data.size(); auto out_sz = out.data.size(); void* in_dev; void* wei_dev; void* out_dev; ASSERT_EQ(hipMalloc(&in_dev, sizeof(TRef) * in_sz), hipSuccess); ASSERT_EQ(hipMalloc(&wei_dev, sizeof(TRef) * wei_sz), hipSuccess); ASSERT_EQ(hipMalloc(&out_dev, sizeof(Tout) * out_sz), hipSuccess); ASSERT_EQ(miopenCreateConvolutionDescriptor(&convDesc), miopenStatusSuccess); ASSERT_EQ(miopenInitConvolutionNdDescriptor(convDesc, 2, static_cast(pads), static_cast(strides), static_cast(dilations), miopenConvolution), miopenStatusSuccess); ASSERT_EQ(miopenSetConvolutionGroupCount(convDesc, param.g), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&inDesc), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&weiDesc), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&outDesc), miopenStatusSuccess); ASSERT_EQ(miopenSetTensorDescriptor( inDesc, miopen_type{}, in_len.size(), in_len.data(), in_strides.data()), miopenStatusSuccess); ASSERT_EQ( miopenSetTensorDescriptor( weiDesc, miopen_type{}, wei_len.size(), wei_len.data(), wei_strides.data()), miopenStatusSuccess); ASSERT_EQ( miopenSetTensorDescriptor( outDesc, miopen_type{}, out_len.size(), out_len.data(), out_strides.data()), miopenStatusSuccess); bool valid_result = false; if(direction == miopen::conv::Direction::Forward) { // initialize data with integer rand_tensor_integer(in); rand_tensor_integer(wei); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(out); ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); /// \anchor copy_non_packed_output_before_convolution /// \note Output is a non-packed tensor, which means there are /// elements that convolution will not update. In order to verify /// the convolution result, the GPU buffer should have the same /// data as the CPU in both update and not-updated elements. /// Therefore, we copy the output to the GPU buffer after /// initializing it with random values. /// ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_forward(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); ASSERT_EQ(miopenConvolutionForwardImmediate( handle, weiDesc, wei_dev, inDesc, in_dev, convDesc, outDesc, out_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvFwd").Value()), miopenStatusSuccess); tensor out_host(out_len, out_strides); ASSERT_EQ( hipMemcpy( out_host.data.data(), out_dev, sizeof(Tout) * out_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(out_host, out); } else if(direction == miopen::conv::Direction::BackwardData) { // initialize data with integer rand_tensor_integer(out); rand_tensor_integer(wei); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(in); /// \ref copy_non_packed_output_before_convolution ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_backward_data(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); EXPECT_EQ(miopenConvolutionBackwardDataImmediate( handle, outDesc, out_dev, weiDesc, wei_dev, convDesc, inDesc, in_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvBwd").Value()), miopenStatusSuccess); tensor in_host(in_len, in_strides); ASSERT_EQ( hipMemcpy(in_host.data.data(), in_dev, sizeof(TRef) * in_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(in_host, in); } else if(direction == miopen::conv::Direction::BackwardWeights) { rand_tensor_integer(in); rand_tensor_integer(out); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(wei); ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); /// \ref copy_non_packed_output_before_convolution ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_backward_weight(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); EXPECT_EQ(miopenConvolutionBackwardWeightsImmediate( handle, outDesc, out_dev, inDesc, in_dev, convDesc, weiDesc, wei_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvWrw").Value()), miopenStatusSuccess); tensor wei_host(wei_len, wei_strides); ASSERT_EQ( hipMemcpy( wei_host.data.data(), wei_dev, sizeof(TRef) * wei_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(wei_host, wei); } std::cout << "n:" << param.n << ", c:" << param.c << ", hi:" << param.hi << ", wi:" << param.wi << ", k:" << param.k << ", ho:" << ho << ", wo:" << wo << ", fy:" << param.fy << ",fx:" << param.fx << ", py:" << param.py << ", px:" << param.px << ", sy:" << param.sy << ", sx:" << param.sx << ", dy:" << param.dy << ",dx:" << param.dx << ", g:" << param.g << ", dir:" << direction_to_string(direction) << ", type:" << miopen_type_to_string(miopen_type{}) << ", layout:" << layout_string << ", valid:" << valid_result << std::endl; EXPECT_EQ(valid_result, true); miopenDestroyConvolutionDescriptor(convDesc); miopenDestroyTensorDescriptor(inDesc); miopenDestroyTensorDescriptor(weiDesc); miopenDestroyTensorDescriptor(outDesc); (void)hipFree(in_dev); (void)hipFree(wei_dev); (void)hipFree(out_dev); } }; template struct gpu_reference_conv_3d : public ::testing::TestWithParam { miopenHandle_t handle{}; void SetUp() override { prng::reset_seed(); miopenCreate(&handle); } void TearDown() override { miopenDestroy(handle); } void Run() { miopenConvolutionDescriptor_t convDesc; miopenTensorDescriptor_t inDesc, weiDesc, outDesc; auto const& param = GetParam(); int pads[] = {param.pz, param.py, param.px}; int strides[] = {param.sz, param.sy, param.sx}; int dilations[] = {param.dz, param.dy, param.dx}; int ho = conv_out_size(param.hi, param.py, param.dy, param.fy, param.sy); int wo = conv_out_size(param.wi, param.px, param.dx, param.fx, param.sx); int do_ = conv_out_size(param.di, param.pz, param.dz, param.fz, param.sz); int c_per_group = param.c / param.g; std::vector in_len({param.n, param.c, param.di, param.hi, param.wi}); std::vector wei_len({param.k, c_per_group, param.fz, param.fy, param.fx}); std::vector out_len({param.n, param.k, do_, ho, wo}); std::vector in_strides; std::vector wei_strides; std::vector out_strides; std::string layout_default = miopen::tensor_layout_get_default(5); std::string layout_string = miopen::TensorDescriptor::LayoutEnumToStr(tensor_layout); miopen::tensor_layout_to_strides(in_len, layout_default, layout_string, in_strides); miopen::tensor_layout_to_strides(wei_len, layout_default, layout_string, wei_strides); miopen::tensor_layout_to_strides(out_len, layout_default, layout_string, out_strides); pad_tensor_strides(in_strides); pad_tensor_strides(wei_strides); pad_tensor_strides(out_strides); tensor in(in_len, in_strides); tensor wei(wei_len, wei_strides); tensor out(out_len, out_strides); auto in_sz = in.data.size(); auto wei_sz = wei.data.size(); auto out_sz = out.data.size(); void* in_dev; void* wei_dev; void* out_dev; ASSERT_EQ(hipMalloc(&in_dev, sizeof(TRef) * in_sz), hipSuccess); ASSERT_EQ(hipMalloc(&wei_dev, sizeof(TRef) * wei_sz), hipSuccess); ASSERT_EQ(hipMalloc(&out_dev, sizeof(Tout) * out_sz), hipSuccess); ASSERT_EQ(miopenCreateConvolutionDescriptor(&convDesc), miopenStatusSuccess); ASSERT_EQ(miopenInitConvolutionNdDescriptor(convDesc, 3, static_cast(pads), static_cast(strides), static_cast(dilations), miopenConvolution), miopenStatusSuccess); ASSERT_EQ(miopenSetConvolutionGroupCount(convDesc, param.g), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&inDesc), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&weiDesc), miopenStatusSuccess); ASSERT_EQ(miopenCreateTensorDescriptor(&outDesc), miopenStatusSuccess); ASSERT_EQ(miopenSetTensorDescriptor( inDesc, miopen_type{}, in_len.size(), in_len.data(), in_strides.data()), miopenStatusSuccess); ASSERT_EQ( miopenSetTensorDescriptor( weiDesc, miopen_type{}, wei_len.size(), wei_len.data(), wei_strides.data()), miopenStatusSuccess); ASSERT_EQ( miopenSetTensorDescriptor( outDesc, miopen_type{}, out_len.size(), out_len.data(), out_strides.data()), miopenStatusSuccess); bool valid_result = false; if(direction == miopen::conv::Direction::Forward) { // initialize data with integer rand_tensor_integer(in); rand_tensor_integer(wei); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(out); ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); /// \ref copy_non_packed_output_before_convolution ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_forward(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); ASSERT_EQ(miopenConvolutionForwardImmediate( handle, weiDesc, wei_dev, inDesc, in_dev, convDesc, outDesc, out_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvFwd").Value()), miopenStatusSuccess); tensor out_host(out_len, out_strides); ASSERT_EQ( hipMemcpy( out_host.data.data(), out_dev, sizeof(Tout) * out_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(out_host, out); } else if(direction == miopen::conv::Direction::BackwardData) { // initialize data with integer rand_tensor_integer(out); rand_tensor_integer(wei); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(in); /// \ref copy_non_packed_output_before_convolution ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_backward_data(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); ASSERT_EQ(miopenConvolutionBackwardDataImmediate( handle, outDesc, out_dev, weiDesc, wei_dev, convDesc, inDesc, in_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvBwd").Value()), miopenStatusSuccess); tensor in_host(in_len, in_strides); ASSERT_EQ( hipMemcpy(in_host.data.data(), in_dev, sizeof(TRef) * in_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(in_host, in); } else if(direction == miopen::conv::Direction::BackwardWeights) { rand_tensor_integer(in, 3, -2); rand_tensor_integer(out, 3, -2); /// \ref copy_non_packed_output_before_convolution rand_tensor_integer(wei); ASSERT_EQ( hipMemcpy(in_dev, in.data.data(), sizeof(TRef) * in_sz, hipMemcpyHostToDevice), hipSuccess); /// \ref copy_non_packed_output_before_convolution ASSERT_EQ( hipMemcpy(wei_dev, wei.data.data(), sizeof(TRef) * wei_sz, hipMemcpyHostToDevice), hipSuccess); ASSERT_EQ( hipMemcpy(out_dev, out.data.data(), sizeof(Tout) * out_sz, hipMemcpyHostToDevice), hipSuccess); cpu_convolution_backward_weight(miopen::deref(convDesc).GetSpatialDimension(), in, wei, out, miopen::deref(convDesc).GetConvPads(), miopen::deref(convDesc).GetConvStrides(), miopen::deref(convDesc).GetConvDilations(), miopen::deref(convDesc).GetGroupCount()); ASSERT_EQ(miopenConvolutionBackwardWeightsImmediate( handle, outDesc, out_dev, inDesc, in_dev, convDesc, weiDesc, wei_dev, nullptr, 0, miopen::solver::Id("ConvDirectNaiveConvWrw").Value()), miopenStatusSuccess); tensor wei_host(wei_len, wei_strides); ASSERT_EQ( hipMemcpy( wei_host.data.data(), wei_dev, sizeof(TRef) * wei_sz, hipMemcpyDeviceToHost), hipSuccess); // we expect excact match, since use integer valid_result = verify_tensor(wei_host, wei, 8.0); // max possible int // 2*14*14*10*(2*2) = 15680, hence // int interval might be 8 } // auto error = miopen::rms_range(out_host.data, out.data); // auto tolerance = get_default_tolerence(); // bool valid_result = error <= tolerance; std::cout << "n:" << param.n << ", c:" << param.c << ", di:" << param.di << ", hi:" << param.hi << ", wi:" << param.wi << ", k:" << param.k << ", do:" << do_ << ", ho:" << ho << ", wo:" << wo << ", fz:" << param.fz << ", fy:" << param.fy << ",fx:" << param.fx << ", pz:" << param.pz << ", py:" << param.py << ", px:" << param.px << ", sz:" << param.sz << ", sy:" << param.sy << ", sx:" << param.sx << ", dz:" << param.dz << ", dy:" << param.dy << ", dx:" << param.dx << ", g:" << param.g << ", dir:" << direction_to_string(direction) << ", type:" << miopen_type_to_string(miopen_type{}) << ", layout:" << layout_string << ", valid:" << valid_result << std::endl; EXPECT_EQ(valid_result, true); miopenDestroyConvolutionDescriptor(convDesc); miopenDestroyTensorDescriptor(inDesc); miopenDestroyTensorDescriptor(weiDesc); miopenDestroyTensorDescriptor(outDesc); (void)hipFree(in_dev); (void)hipFree(wei_dev); (void)hipFree(out_dev); } }; } // namespace // 2d NCHW using GPU_reference_kernel_fwd_2d_NCHW_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NCHW_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NCHW_BFP16_BFP16 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NCHW_I8_I32 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NCHW_I8_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NCHW_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NCHW_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NCHW_BFP16_BFP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NCHW_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NCHW_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NCHW_BFP16_BFP16 = gpu_reference_conv_2d; // 3d NCDHW using GPU_reference_kernel_fwd_3d_NCDHW_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NCDHW_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NCDHW_BFP16_BFP16 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NCDHW_I8_I32 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NCDHW_I8_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NCDHW_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NCDHW_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NCDHW_BFP16_BFP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NCDHW_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NCDHW_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NCDHW_BFP16_BFP16 = gpu_reference_conv_3d; // 2d NHWC using GPU_reference_kernel_fwd_2d_NHWC_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NHWC_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NHWC_BFP16_BFP16 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NHWC_I8_I32 = gpu_reference_conv_2d; using GPU_reference_kernel_fwd_2d_NHWC_I8_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NHWC_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NHWC_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bwd_2d_NHWC_BFP16_BFP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NHWC_FP32_FP32 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NHWC_FP16_FP16 = gpu_reference_conv_2d; using GPU_reference_kernel_bww_2d_NHWC_BFP16_BFP16 = gpu_reference_conv_2d; // 3d NCDHW using GPU_reference_kernel_fwd_3d_NDHWC_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NDHWC_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NDHWC_BFP16_BFP16 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NDHWC_I8_I32 = gpu_reference_conv_3d; using GPU_reference_kernel_fwd_3d_NDHWC_I8_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NDHWC_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NDHWC_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bwd_3d_NDHWC_BFP16_BFP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NDHWC_FP32_FP32 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NDHWC_FP16_FP16 = gpu_reference_conv_3d; using GPU_reference_kernel_bww_3d_NDHWC_BFP16_BFP16 = gpu_reference_conv_3d; TEST_P(GPU_reference_kernel_fwd_2d_NCHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NCHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NCHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NCHW_I8_I32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NCHW_I8_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NCHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NCHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NCHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NCHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NCHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NCHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NCDHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NCDHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NCDHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NCDHW_I8_I32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NCDHW_I8_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NCDHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NCDHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NCDHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NCDHW_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NCDHW_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NCDHW_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NHWC_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NHWC_I8_I32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_2d_NHWC_I8_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_2d_NHWC_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_2d_NHWC_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NDHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NDHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NDHWC_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NDHWC_I8_I32, Test) { Run(); } TEST_P(GPU_reference_kernel_fwd_3d_NDHWC_I8_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NDHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NDHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bwd_3d_NDHWC_BFP16_BFP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NDHWC_FP32_FP32, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NDHWC_FP16_FP16, Test) { Run(); } TEST_P(GPU_reference_kernel_bww_3d_NDHWC_BFP16_BFP16, Test) { Run(); } INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NCHW_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NCHW_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NCHW_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NCHW_I8_I32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NCHW_I8_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NCHW_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NCHW_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NCHW_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NCHW_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NCHW_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NCHW_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NCDHW_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NCDHW_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NCDHW_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NCDHW_I8_I32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NCDHW_I8_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NCDHW_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NCDHW_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NCDHW_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NCDHW_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NCDHW_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NCDHW_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NHWC_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NHWC_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NHWC_I8_I32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_2d_NHWC_I8_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NHWC_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NHWC_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_2d_NHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NHWC_FP32_FP32, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NHWC_FP16_FP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_2d_NHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases2D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NDHWC_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NDHWC_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NDHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NDHWC_I8_I32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_fwd_3d_NDHWC_I8_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NDHWC_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NDHWC_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bwd_3d_NDHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NDHWC_FP32_FP32, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NDHWC_FP16_FP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); }); INSTANTIATE_TEST_SUITE_P(Smoke, GPU_reference_kernel_bww_3d_NDHWC_BFP16_BFP16, ::testing::ValuesIn(GenCases3D()), [](const auto& _info) { return NameGenerator(_info); });