CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
2479 lines
99 KiB
C++
2479 lines
99 KiB
C++
/*******************************************************************************
|
|
*
|
|
* MIT License
|
|
*
|
|
* Copyright (c) 2019 Advanced Micro Devices, Inc.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*******************************************************************************/
|
|
#pragma once
|
|
#include "test.hpp"
|
|
#include "workspace.hpp"
|
|
#include <array>
|
|
#include <iostream>
|
|
#include <iterator>
|
|
#include <limits>
|
|
#include <memory>
|
|
|
|
#include <miopen/convolution.hpp>
|
|
#include <miopen/miopen.h>
|
|
#include <miopen/tensor.hpp>
|
|
#include <miopen/tensor_layout.hpp>
|
|
#include <miopen/tensor_ops.hpp>
|
|
#include <miopen/mlo_internal.hpp>
|
|
#include <miopen/solution.hpp>
|
|
#include <miopen/invoke_params.hpp>
|
|
#include <miopen/conv/tensors.hpp>
|
|
#include <miopen/conv/data_invoke_params.hpp>
|
|
#include <miopen/any_solver.hpp>
|
|
|
|
#include <utility>
|
|
#include "driver.hpp"
|
|
#include "get_handle.hpp"
|
|
#include "tensor_holder.hpp"
|
|
#include "verify.hpp"
|
|
#include <miopen/stringutils.hpp>
|
|
#include "tensor_util.hpp"
|
|
#include <miopen/algorithm.hpp>
|
|
#include "cpu_conv.hpp"
|
|
#include "gpu_conv.hpp"
|
|
#include "network_data.hpp"
|
|
#include "miopen/find_db.hpp"
|
|
#include "random.hpp"
|
|
|
|
#define TEST_DIRECT_SUPPORTED_CONFIG_ONLY (!MIOPEN_USE_ROCBLAS)
|
|
|
|
using ExecutionContext = miopen::ExecutionContext;
|
|
using ConvProblemDescription = miopen::conv::ProblemDescription;
|
|
using Direction = miopen::conv::Direction;
|
|
|
|
bool get_handle_xnack();
|
|
|
|
#if TEST_DIRECT_SUPPORTED_CONFIG_ONLY
|
|
static inline bool is_direct_fwd_bwd_data_supported(const miopen::Handle& handle,
|
|
const miopen::ConvolutionDescriptor convDesc,
|
|
const miopen::TensorDescriptor& xDesc,
|
|
const miopen::TensorDescriptor& wDesc,
|
|
const miopen::TensorDescriptor& yDesc)
|
|
{
|
|
if(convDesc.GetSpatialDimension() != 2)
|
|
return false;
|
|
|
|
// Both Fwd and Bwd shall be supported by Direct. Return false otherwise.
|
|
for(int direction = 1; direction >= 0; --direction)
|
|
{
|
|
const auto dir = static_cast<miopen::conv::Direction>(direction);
|
|
const auto problem =
|
|
(dir == miopen::conv::Direction::Forward)
|
|
? miopen::conv::ProblemDescription{xDesc, wDesc, yDesc, convDesc, dir}
|
|
: miopen::conv::ProblemDescription{yDesc, wDesc, xDesc, convDesc, dir};
|
|
auto ctx = miopen::ExecutionContext{};
|
|
ctx.do_search = false;
|
|
ctx.disable_perfdb_access = true;
|
|
ctx.general_compile_options = "";
|
|
ctx.SetStream(&handle);
|
|
problem.SetupFloats(ctx);
|
|
problem.SetupComputeType(ctx);
|
|
if(FindAllDirectSolutions(ctx, problem, {}).empty())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static inline bool is_direct_bwd_wrw_supported(const miopen::Handle& handle,
|
|
const miopen::ConvolutionDescriptor convDesc,
|
|
const miopen::TensorDescriptor& xDesc,
|
|
const miopen::TensorDescriptor& wDesc,
|
|
const miopen::TensorDescriptor& yDesc)
|
|
{
|
|
if(convDesc.GetSpatialDimension() != 2)
|
|
return false;
|
|
|
|
const auto problem = miopen::conv::ProblemDescription{
|
|
yDesc, wDesc, xDesc, convDesc, miopen::conv::Direction::BackwardWeights};
|
|
auto ctx = miopen::ExecutionContext{};
|
|
|
|
ctx.do_search = false;
|
|
ctx.general_compile_options = "";
|
|
ctx.disable_perfdb_access = true;
|
|
ctx.SetStream(&handle);
|
|
problem.SetupFloats(ctx);
|
|
problem.SetupComputeType(ctx);
|
|
|
|
return !FindAllBwdWrW2DSolutions(ctx, problem, {}).empty();
|
|
}
|
|
#endif
|
|
|
|
inline miopenTensorLayout_t
|
|
StringToLayoutType(std::string layout_str, int tensor_vect, int vector_length)
|
|
{
|
|
miopenTensorLayout_t default_layout = miopenTensorNCHW;
|
|
if(tensor_vect == 0)
|
|
{
|
|
if(layout_str == "NCHW")
|
|
{
|
|
return miopenTensorNCHW;
|
|
}
|
|
else if(layout_str == "NHWC")
|
|
{
|
|
return miopenTensorNHWC;
|
|
}
|
|
else if(layout_str == "NDHWC")
|
|
{
|
|
return miopenTensorNDHWC;
|
|
}
|
|
else if(layout_str == "NCDHW")
|
|
{
|
|
return miopenTensorNCDHW;
|
|
}
|
|
else
|
|
{
|
|
MIOPEN_THROW("Non-vectorized tensor only support layout NCHW, NHWC, NCDHW and NDHWC");
|
|
return default_layout;
|
|
}
|
|
}
|
|
else if(tensor_vect == 1)
|
|
{
|
|
if(vector_length == 4)
|
|
{
|
|
return layout_str == "CHWN" ? miopenTensorCHWNc4 : miopenTensorNCHWc4;
|
|
}
|
|
else if(vector_length == 8)
|
|
{
|
|
return layout_str == "CHWN" ? miopenTensorCHWNc8 : miopenTensorNCHWc8;
|
|
}
|
|
else
|
|
{
|
|
MIOPEN_THROW("C-vectorized tensor only support vector length 4 and 8");
|
|
return default_layout;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MIOPEN_THROW("MIOpen only support Non-vectorized and C-vectorized tensor");
|
|
return default_layout;
|
|
}
|
|
}
|
|
|
|
struct conv_stats
|
|
{
|
|
std::string solver_name{};
|
|
miopenConvAlgorithm_t algorithm = static_cast<miopenConvAlgorithm_t>(-1);
|
|
};
|
|
|
|
template <class T, class Tout = T>
|
|
tensor<Tout> get_output_tensor(const miopen::ConvolutionDescriptor& filter,
|
|
const tensor<T>& input,
|
|
const tensor<T>& weights,
|
|
const std::string& out_layout)
|
|
{
|
|
|
|
std::string yLayout = out_layout.empty() ? input.desc.GetLayout_str() : out_layout;
|
|
return tensor<Tout>{filter.GetForwardOutputTensorWithLayout(
|
|
input.desc, weights.desc, yLayout, miopen_type<Tout>{})};
|
|
}
|
|
|
|
enum class ConvApi
|
|
{
|
|
Invalid,
|
|
Find_1_0,
|
|
Immediate,
|
|
Find_2_0,
|
|
};
|
|
|
|
struct ProblemDestructor
|
|
{
|
|
void operator()(miopenProblem_t ptr) { miopenDestroyProblem(ptr); }
|
|
};
|
|
|
|
struct ProblemWrapper : std::unique_ptr<miopenProblem, ProblemDestructor>
|
|
{
|
|
ProblemWrapper(miopenConvolutionDescriptor_t conv, miopenProblemDirection_t direction)
|
|
{
|
|
miopenProblem_t value;
|
|
EXPECT_EQUAL(miopenStatusSuccess, miopenCreateConvProblem(&value, conv, direction));
|
|
this->reset(value);
|
|
}
|
|
};
|
|
|
|
struct FindOptionsDestructor
|
|
{
|
|
void operator()(miopenFindOptions_t ptr) { miopenDestroyFindOptions(ptr); }
|
|
};
|
|
|
|
struct FindOptionsWrapper : std::unique_ptr<miopenFindOptions, FindOptionsDestructor>
|
|
{
|
|
FindOptionsWrapper()
|
|
{
|
|
miopenFindOptions_t value;
|
|
EXPECT_EQUAL(miopenStatusSuccess, miopenCreateFindOptions(&value));
|
|
this->reset(value);
|
|
}
|
|
};
|
|
|
|
// Convolution test base class
|
|
//========================================
|
|
template <class T, class Tout = T>
|
|
struct conv_base
|
|
{
|
|
tensor<T> input;
|
|
tensor<T> weights;
|
|
tensor<Tout> out;
|
|
miopen::ConvolutionDescriptor filter;
|
|
int bias{};
|
|
int search{};
|
|
bool enable_fdb{};
|
|
int conv_spatial_dims{};
|
|
conv_stats* stats{}; // Denotes an object after object construction (never nullptr).
|
|
bool preallocate;
|
|
|
|
conv_base(bool preallocate_) : preallocate(preallocate_) {}
|
|
|
|
void fail(float = 0) const
|
|
{
|
|
std::cout << "Input tensor: " << input.desc.ToString() << std::endl;
|
|
std::cout << "Weights tensor: " << weights.desc.ToString() << std::endl;
|
|
std::cout << "Output tensor: " << out.desc.ToString() << std::endl;
|
|
std::cout << "Filter: " << filter << std::endl;
|
|
}
|
|
|
|
protected:
|
|
void RunFind2_0(miopenProblem_t problem, const miopenTensorArgument_t* arguments) const
|
|
{
|
|
miopenHandle_t handle = &get_handle();
|
|
|
|
constexpr const auto find_limit = 1;
|
|
std::size_t found;
|
|
|
|
auto solutions = std::vector<miopenSolution_t>{};
|
|
solutions.resize(find_limit);
|
|
|
|
{
|
|
const auto options = MakeOptions();
|
|
|
|
if(preallocate)
|
|
{
|
|
for(auto i = 0; i < 3; ++i)
|
|
{
|
|
miopenSetFindOptionPreallocatedTensor(
|
|
options.get(), arguments[i].id, arguments[i].buffer);
|
|
}
|
|
}
|
|
|
|
EXPECT_EQUAL(
|
|
miopenStatusSuccess,
|
|
miopenFindSolutions(
|
|
handle, problem, options.get(), solutions.data(), &found, solutions.size()));
|
|
}
|
|
|
|
EXPECT_OP(found, >=, 0);
|
|
|
|
solutions.resize(found);
|
|
|
|
for(const auto& solution : solutions)
|
|
{
|
|
auto workspace_size = std::size_t{};
|
|
EXPECT_EQUAL(miopenStatusSuccess,
|
|
miopenGetSolutionWorkspaceSize(solution, &workspace_size));
|
|
|
|
Workspace wspace{workspace_size};
|
|
|
|
EXPECT_EQUAL(
|
|
miopenStatusSuccess,
|
|
miopenRunSolution(handle, solution, 3, arguments, wspace.ptr(), wspace.size()));
|
|
}
|
|
|
|
const auto& solution_deref = miopen::deref(solutions.front());
|
|
|
|
stats->solver_name = solution_deref.GetSolver().ToString();
|
|
stats->algorithm = solution_deref.GetSolver().GetAlgo();
|
|
|
|
for(const auto& solution : solutions)
|
|
{
|
|
EXPECT_EQUAL(miopenStatusSuccess, miopenDestroySolution(solution));
|
|
}
|
|
}
|
|
|
|
ProblemWrapper MakeConvProblem(miopenProblemDirection_t direction,
|
|
miopenTensorDescriptor_t x,
|
|
miopenTensorDescriptor_t w,
|
|
miopenTensorDescriptor_t y)
|
|
{
|
|
auto problem = ProblemWrapper{&filter, direction};
|
|
|
|
miopenSetProblemTensorDescriptor(problem.get(), miopenTensorConvolutionX, x);
|
|
miopenSetProblemTensorDescriptor(problem.get(), miopenTensorConvolutionW, w);
|
|
miopenSetProblemTensorDescriptor(problem.get(), miopenTensorConvolutionY, y);
|
|
|
|
return problem;
|
|
}
|
|
|
|
private:
|
|
FindOptionsWrapper MakeOptions() const
|
|
{
|
|
auto search_options = FindOptionsWrapper{};
|
|
|
|
EXPECT_EQUAL(miopenStatusSuccess, miopenSetFindOptionTuning(search_options.get(), search));
|
|
|
|
return search_options;
|
|
}
|
|
};
|
|
|
|
template <typename T, typename Tout = T>
|
|
tensor<Tout> ref_conv_fwd(const tensor<T>& input,
|
|
const tensor<T>& weights,
|
|
const tensor<Tout>& out,
|
|
const miopen::ConvolutionDescriptor& filter,
|
|
const miopen::Scalar& alpha = miopen::Scalar(1.0),
|
|
const miopen::Scalar& beta = miopen::Scalar(0.0))
|
|
{
|
|
auto rout = out;
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
std::fill(rout.begin(), rout.end(), static_cast<Tout>(0));
|
|
bool gpu_ref_used = gpu_ref_convolution_bwd(rout, weights, input, filter);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference skipped");
|
|
cpu_convolution_backward_data(filter.GetSpatialDimension(),
|
|
rout,
|
|
weights,
|
|
input,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_fwd(input, weights, rout, filter, alpha, beta);
|
|
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference skipped");
|
|
cpu_convolution_forward(filter.GetSpatialDimension(),
|
|
input,
|
|
weights,
|
|
rout,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
return rout;
|
|
}
|
|
|
|
template <typename T, typename Twei, typename Tout>
|
|
tensor<Twei> ref_conv_wrw(const tensor<T>& input,
|
|
const tensor<Twei>& weights,
|
|
const tensor<Tout>& out,
|
|
const miopen::ConvolutionDescriptor& filter,
|
|
const miopen::Scalar& alpha = miopen::Scalar(1.0),
|
|
const miopen::Scalar& beta = miopen::Scalar(0.0))
|
|
{
|
|
auto rwei = weights;
|
|
std::fill(rwei.begin(), rwei.end(), 0);
|
|
bool gpu_ref_used = gpu_ref_convolution_wrw(input, rwei, out, filter, alpha, beta);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference skipped");
|
|
cpu_convolution_backward_weight(filter.GetSpatialDimension(),
|
|
input,
|
|
rwei,
|
|
out,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
return rwei;
|
|
}
|
|
|
|
template <typename T, typename Tout = T>
|
|
tensor<Tout> ref_conv_bwd(const tensor<Tout>& input,
|
|
const tensor<T>& weights,
|
|
const tensor<T>& out,
|
|
const miopen::ConvolutionDescriptor& filter,
|
|
const miopen::Scalar& alpha = miopen::Scalar(1.0),
|
|
const miopen::Scalar& beta = miopen::Scalar(0.0))
|
|
{
|
|
auto rinput = input;
|
|
|
|
std::fill(rinput.begin(), rinput.end(), 0);
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_fwd(out, weights, rinput, filter, alpha, beta);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_forward(filter.GetSpatialDimension(),
|
|
out,
|
|
weights,
|
|
rinput,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_bwd(rinput, weights, out, filter, alpha, beta);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_data(filter.GetSpatialDimension(),
|
|
rinput,
|
|
weights,
|
|
out,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
return rinput;
|
|
}
|
|
|
|
template <typename T, typename Tout = T>
|
|
tensor<Tout> ref_conv_wrw(const tensor<T>& input,
|
|
const tensor<Tout>& weights,
|
|
const tensor<T>& out,
|
|
const miopen::ConvolutionDescriptor& filter,
|
|
const miopen::Scalar& alpha = miopen::Scalar(1.0),
|
|
const miopen::Scalar& beta = miopen::Scalar(0.0))
|
|
{
|
|
auto rweights = weights;
|
|
std::fill(rweights.begin(), rweights.end(), 0);
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_wrw(out, rweights, input, filter, alpha, beta);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_weight(filter.GetSpatialDimension(),
|
|
out,
|
|
rweights,
|
|
input,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_wrw(input, rweights, out, filter, alpha, beta);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_weight(filter.GetSpatialDimension(),
|
|
input,
|
|
rweights,
|
|
out,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
return rweights;
|
|
}
|
|
|
|
// Mainline convolution tests
|
|
//========================================
|
|
template <ConvApi api, class T, class Tout = T>
|
|
struct verify_forward_conv : conv_base<T, Tout>
|
|
{
|
|
using conv_base<T, Tout>::input;
|
|
using conv_base<T, Tout>::weights;
|
|
using conv_base<T, Tout>::out;
|
|
using conv_base<T, Tout>::filter;
|
|
using conv_base<T, Tout>::bias;
|
|
using conv_base<T, Tout>::search;
|
|
using conv_base<T, Tout>::stats;
|
|
|
|
using conv_base<T, Tout>::RunFind2_0;
|
|
using conv_base<T, Tout>::MakeConvProblem;
|
|
|
|
bool is_vect;
|
|
|
|
verify_forward_conv(const tensor<T>& pinput,
|
|
const tensor<T>& pweights,
|
|
const tensor<Tout>& pout,
|
|
const miopen::ConvolutionDescriptor& pfilter,
|
|
conv_stats& pstats,
|
|
bool preallocate_,
|
|
int pbias,
|
|
int psearch,
|
|
bool pvect)
|
|
: conv_base<T, Tout>(preallocate_)
|
|
{
|
|
input = pinput;
|
|
weights = pweights;
|
|
out = pout;
|
|
filter = pfilter;
|
|
bias = pbias;
|
|
search = psearch;
|
|
is_vect = pvect;
|
|
stats = &pstats;
|
|
}
|
|
|
|
tensor<Tout> cpu() const
|
|
{
|
|
auto rout = ref_conv_fwd(input, weights, out, filter);
|
|
if(filter.mode != miopenTranspose)
|
|
{
|
|
bool is_int8 = weights.desc.GetType() == miopenInt8;
|
|
bool is_vect_c = weights.desc.GetVectorLength() > 1;
|
|
rout.par_for_each([&](auto... is) {
|
|
if(is_int8 && !is_vect_c)
|
|
{
|
|
rout(is...) = Tout(double(rout(is...)) + double(this->bias));
|
|
}
|
|
else if(is_vect_c)
|
|
{
|
|
for(std::size_t i = 0; i < weights.desc.GetVectorLength(); i++)
|
|
rout(i, is...) = double(rout(i, is...)) + double(this->bias);
|
|
}
|
|
else
|
|
rout(is...) = double(rout(is...)) + double(this->bias);
|
|
});
|
|
}
|
|
|
|
return rout;
|
|
}
|
|
|
|
tensor<Tout> gpu()
|
|
{
|
|
auto&& handle = get_handle();
|
|
auto rout = out;
|
|
|
|
auto in_dev = handle.Write(input.data);
|
|
auto wei_dev = handle.Write(weights.data);
|
|
auto out_dev = handle.Write(rout.data);
|
|
|
|
/// \section read_solver_name
|
|
/// Find() updates find-db with the most recent information (unless find-db is
|
|
/// disabled). Therefore, after Find(), Immediate mode returns the "best" found solution
|
|
/// as the 1st solution in the list, and we can use Immediate mode to find out
|
|
/// the name of the Solver selected during Find() and then used in Run().
|
|
/// So we use one Immediate mode call during Find mode tests,
|
|
/// to print solver name onto console.
|
|
miopenConvSolution_t selected = {};
|
|
auto fallback_path_taken = miopen::FallbackPath();
|
|
std::size_t count = 0;
|
|
|
|
Workspace wspace{};
|
|
|
|
const auto ctx = ExecutionContext{&handle};
|
|
const auto problem = ConvProblemDescription{
|
|
input.desc,
|
|
weights.desc,
|
|
rout.desc,
|
|
filter,
|
|
filter.mode != miopenTranspose ? Direction::Forward : Direction::BackwardData};
|
|
|
|
switch(api)
|
|
{
|
|
case ConvApi::Immediate:
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
filter.FindConvBwdDataAlgorithm(handle,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount."
|
|
<< std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
const auto solutions =
|
|
filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
selected = solutions.front();
|
|
|
|
{
|
|
const std::size_t ws_size = filter.GetBackwardSolutionWorkspaceSize(
|
|
handle, input.desc, weights.desc, rout.desc, selected.solution_id);
|
|
if(ws_size != selected.workspace_size)
|
|
{
|
|
std::cout << "WARNING: workspace size mismatch: " << selected.workspace_size
|
|
<< " != " << ws_size << std::endl;
|
|
}
|
|
}
|
|
wspace.resize(selected.workspace_size);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
filter.ConvolutionBackwardImmediate(handle,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
}
|
|
else
|
|
{
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
|
|
count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount."
|
|
<< std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
// std::cout << "Forward Conv solutions available: " << count << std::endl;
|
|
auto solutions = filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
selected = std::move(solutions.front());
|
|
|
|
{
|
|
const std::size_t ws_size = filter.GetForwardSolutionWorkspaceSize(
|
|
handle, weights.desc, input.desc, rout.desc, selected.solution_id);
|
|
if(ws_size != selected.workspace_size)
|
|
{
|
|
std::cout << "WARNING: workspace size mismatch: " << selected.workspace_size
|
|
<< " != " << ws_size << std::endl;
|
|
}
|
|
}
|
|
wspace.resize(selected.workspace_size);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
filter.ConvolutionForwardImmediate(handle,
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
input.desc,
|
|
in_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
}
|
|
break;
|
|
case ConvApi::Find_1_0:
|
|
case ConvApi::Find_2_0:
|
|
if(weights.desc.GetType() == miopenInt8)
|
|
{
|
|
|
|
bool is_transform = (input.desc.GetLengths()[1] % 4 != 0 || is_vect);
|
|
|
|
std::vector<std::size_t> in_len(input.desc.GetLengths().begin(),
|
|
input.desc.GetLengths().end());
|
|
std::vector<std::size_t> wei_len(weights.desc.GetLengths().begin(),
|
|
weights.desc.GetLengths().end());
|
|
in_len[1] = ((in_len[1] + 3) / 4) * 4;
|
|
wei_len[1] = ((wei_len[1] + 3) / 4) * 4;
|
|
|
|
miopen::TensorDescriptor input_vpad_desc(miopenInt8, in_len);
|
|
miopen::TensorDescriptor weight_vpad_desc(miopenInt8, wei_len);
|
|
|
|
auto input_vpad = tensor<T>{in_len};
|
|
auto weights_vpad = tensor<T>{wei_len};
|
|
auto in_vpad_dev = handle.Write(input_vpad.data);
|
|
auto wei_vpad_dev = handle.Write(weights_vpad.data);
|
|
|
|
if(is_transform)
|
|
{
|
|
float aph = 1.0;
|
|
float bta = 0.0;
|
|
TransformTensor(handle,
|
|
&aph,
|
|
input.desc,
|
|
in_dev.get(),
|
|
&bta,
|
|
input_vpad_desc,
|
|
in_vpad_dev.get());
|
|
|
|
TransformTensor(handle,
|
|
&aph,
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
&bta,
|
|
weight_vpad_desc,
|
|
wei_vpad_dev.get());
|
|
}
|
|
|
|
auto in_desc = (is_transform ? input_vpad_desc : input.desc);
|
|
auto wei_desc = (is_transform ? weight_vpad_desc : weights.desc);
|
|
|
|
auto in_buf = (is_transform ? in_vpad_dev.get() : in_dev.get());
|
|
auto wei_buf = (is_transform ? wei_vpad_dev.get() : wei_dev.get());
|
|
|
|
if(api == ConvApi::Find_1_0)
|
|
{
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
float alpha = 1, beta = 0;
|
|
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
in_desc,
|
|
in_buf,
|
|
wei_desc,
|
|
wei_buf,
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionForward(handle,
|
|
&alpha,
|
|
in_desc,
|
|
in_buf,
|
|
wei_desc,
|
|
wei_buf,
|
|
perf.fwd_algo,
|
|
&beta,
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
|
|
/// \ref read_solver_name
|
|
auto solutions = filter.GetSolutions(ctx, problem, 1, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count > 0)
|
|
selected = std::move(solutions.front());
|
|
}
|
|
else if(api == ConvApi::Find_2_0)
|
|
{
|
|
const auto f2_problem = MakeConvProblem(
|
|
miopenProblemDirectionForward, &in_desc, &wei_desc, &rout.desc);
|
|
|
|
const miopenTensorArgument_t arguments[3] = {
|
|
{miopenTensorConvolutionX, nullptr, in_buf},
|
|
{miopenTensorConvolutionW, nullptr, wei_buf},
|
|
{miopenTensorConvolutionY, nullptr, out_dev.get()},
|
|
};
|
|
|
|
RunFind2_0(f2_problem.get(), arguments);
|
|
}
|
|
else
|
|
{
|
|
MIOPEN_THROW(miopenStatusNotImplemented);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(api == ConvApi::Find_1_0)
|
|
{
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
float alpha = 1, beta = 0;
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
filter.FindConvBwdDataAlgorithm(handle,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionBackwardData(handle,
|
|
&alpha,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
perf.bwd_data_algo,
|
|
&beta,
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
}
|
|
else
|
|
{
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionForward(handle,
|
|
&alpha,
|
|
input.desc,
|
|
in_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
perf.fwd_algo,
|
|
&beta,
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
}
|
|
|
|
/// \ref read_solver_name
|
|
auto solutions = filter.GetSolutions(ctx, problem, 1, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count > 0)
|
|
selected = std::move(solutions.front());
|
|
}
|
|
else if(api == ConvApi::Find_2_0)
|
|
{
|
|
const auto f2_problem = MakeConvProblem(
|
|
miopenProblemDirectionForward, &input.desc, &weights.desc, &rout.desc);
|
|
|
|
const miopenTensorArgument_t arguments[3] = {
|
|
{miopenTensorConvolutionX, nullptr, in_dev.get()},
|
|
{miopenTensorConvolutionW, nullptr, wei_dev.get()},
|
|
{miopenTensorConvolutionY, nullptr, out_dev.get()},
|
|
};
|
|
|
|
RunFind2_0(f2_problem.get(), arguments);
|
|
}
|
|
else
|
|
{
|
|
MIOPEN_THROW(miopenStatusNotImplemented);
|
|
}
|
|
}
|
|
break;
|
|
case ConvApi::Invalid: MIOPEN_THROW(miopenStatusInvalidValue);
|
|
}
|
|
|
|
if(count != 0)
|
|
{
|
|
stats->algorithm = selected.algorithm;
|
|
stats->solver_name = miopen::solver::Id(selected.solution_id).ToString();
|
|
if(fallback_path_taken != miopen::FallbackPath::None)
|
|
stats->solver_name += "_fallback";
|
|
}
|
|
rout.data = handle.Read<Tout>(out_dev, rout.data.size());
|
|
return rout;
|
|
}
|
|
|
|
void fail(float = 0) const
|
|
{
|
|
std::cout << "Forward convolution: " << stats->solver_name << std::endl;
|
|
this->conv_base<T, Tout>::fail();
|
|
}
|
|
};
|
|
|
|
template <ConvApi api, class T>
|
|
struct verify_backward_conv : conv_base<T>
|
|
{
|
|
using conv_base<T>::input;
|
|
using conv_base<T>::weights;
|
|
using conv_base<T>::out;
|
|
using conv_base<T>::filter;
|
|
using conv_base<T>::bias;
|
|
using conv_base<T>::search;
|
|
using conv_base<T>::stats;
|
|
|
|
using conv_base<T>::RunFind2_0;
|
|
using conv_base<T>::MakeConvProblem;
|
|
|
|
verify_backward_conv(const tensor<T>& pinput,
|
|
const tensor<T>& pweights,
|
|
const tensor<T>& pout,
|
|
const miopen::ConvolutionDescriptor& pfilter,
|
|
conv_stats& pstats,
|
|
bool preallocate_,
|
|
int pbias,
|
|
int psearch)
|
|
: conv_base<T>(preallocate_)
|
|
{
|
|
input = pinput;
|
|
weights = pweights;
|
|
out = pout;
|
|
filter = pfilter;
|
|
bias = pbias;
|
|
search = psearch;
|
|
stats = &pstats;
|
|
}
|
|
|
|
tensor<T> cpu() const
|
|
{
|
|
auto rinput = input;
|
|
|
|
std::fill(rinput.begin(), rinput.end(), 0);
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_fwd(out, weights, rinput, filter);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_forward(filter.GetSpatialDimension(),
|
|
out,
|
|
weights,
|
|
rinput,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_bwd(rinput, weights, out, filter);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_data(filter.GetSpatialDimension(),
|
|
rinput,
|
|
weights,
|
|
out,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
return rinput;
|
|
}
|
|
|
|
tensor<T> gpu()
|
|
{
|
|
auto&& handle = get_handle();
|
|
auto rinput = input;
|
|
std::fill(rinput.begin(), rinput.end(), 0);
|
|
|
|
auto out_dev = handle.Write(out.data);
|
|
auto wei_dev = handle.Write(weights.data);
|
|
auto in_dev = handle.Write(rinput.data);
|
|
|
|
Workspace wspace{};
|
|
|
|
miopenConvSolution_t selected;
|
|
auto fallback_path_taken = miopen::FallbackPath();
|
|
std::size_t count = 0;
|
|
|
|
const auto ctx = ExecutionContext{&handle};
|
|
const auto problem = ConvProblemDescription{
|
|
out.desc,
|
|
weights.desc,
|
|
rinput.desc,
|
|
filter,
|
|
filter.mode != miopenTranspose ? Direction::BackwardData : Direction::Forward};
|
|
|
|
switch(api)
|
|
{
|
|
case ConvApi::Immediate: {
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount."
|
|
<< std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
// std::cout << "backward transpose Conv solutions available: " << count <<
|
|
// std::endl;
|
|
auto solutions = filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
std::sort(solutions.begin(), solutions.end(), [](const auto& l, const auto& r) {
|
|
return l.time < r.time;
|
|
});
|
|
selected = std::move(solutions.front());
|
|
|
|
[[maybe_unused]] std::size_t ws_size = filter.GetForwardSolutionWorkspaceSize(
|
|
handle, weights.desc, out.desc, rinput.desc, selected.solution_id);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
if(selected.workspace_size > 0)
|
|
{
|
|
wspace.resize(selected.workspace_size);
|
|
}
|
|
|
|
filter.ConvolutionForwardImmediate(handle,
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
out.desc,
|
|
out_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
}
|
|
else
|
|
{
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
filter.FindConvBwdDataAlgorithm(handle,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount."
|
|
<< std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
// std::cout << "Backward Conv solutions available: " << count << std::endl;
|
|
auto solutions = filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
std::sort(solutions.begin(), solutions.end(), [](const auto& l, const auto& r) {
|
|
return l.time < r.time;
|
|
});
|
|
selected = std::move(solutions.front());
|
|
|
|
[[maybe_unused]] std::size_t ws_size = filter.GetBackwardSolutionWorkspaceSize(
|
|
handle, out.desc, weights.desc, rinput.desc, selected.solution_id);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
if(selected.workspace_size > 0)
|
|
{
|
|
wspace.resize(selected.workspace_size);
|
|
}
|
|
|
|
filter.ConvolutionBackwardImmediate(handle,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
}
|
|
break;
|
|
}
|
|
case ConvApi::Find_1_0: {
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
float alpha = 1, beta = 0;
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionForward(handle,
|
|
&alpha,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
perf.fwd_algo,
|
|
&beta,
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
}
|
|
else
|
|
{
|
|
filter.FindConvBwdDataAlgorithm(handle,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionBackwardData(handle,
|
|
&alpha,
|
|
out.desc,
|
|
out_dev.get(),
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
perf.bwd_data_algo,
|
|
&beta,
|
|
rinput.desc,
|
|
in_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
}
|
|
|
|
/// \ref read_solver_name
|
|
const auto solutions = filter.GetSolutions(ctx, problem, 1, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count > 0)
|
|
selected = std::move(solutions.front());
|
|
break;
|
|
}
|
|
case ConvApi::Find_2_0: {
|
|
const auto f2_problem = MakeConvProblem(
|
|
miopenProblemDirectionBackward, &rinput.desc, &weights.desc, &out.desc);
|
|
|
|
const miopenTensorArgument_t arguments[3] = {
|
|
{miopenTensorConvolutionX, nullptr, in_dev.get()},
|
|
{miopenTensorConvolutionW, nullptr, wei_dev.get()},
|
|
{miopenTensorConvolutionY, nullptr, out_dev.get()},
|
|
};
|
|
|
|
RunFind2_0(f2_problem.get(), arguments);
|
|
break;
|
|
}
|
|
case ConvApi::Invalid: MIOPEN_THROW(miopenStatusInvalidValue);
|
|
}
|
|
|
|
if(count != 0)
|
|
{
|
|
stats->algorithm = selected.algorithm;
|
|
stats->solver_name = miopen::solver::Id(selected.solution_id).ToString();
|
|
if(fallback_path_taken != miopen::FallbackPath::None)
|
|
stats->solver_name += "_fallback";
|
|
}
|
|
rinput.data = handle.Read<T>(in_dev, rinput.data.size());
|
|
return rinput;
|
|
}
|
|
|
|
void fail(float) const
|
|
{
|
|
std::cout << "Backward convolution: " << stats->solver_name << std::endl;
|
|
this->conv_base<T>::fail();
|
|
}
|
|
};
|
|
|
|
template <ConvApi api, class T>
|
|
struct verify_backward_weights_conv : conv_base<T>
|
|
{
|
|
using conv_base<T>::input;
|
|
using conv_base<T>::weights;
|
|
using conv_base<T>::out;
|
|
using conv_base<T>::filter;
|
|
using conv_base<T>::bias;
|
|
using conv_base<T>::search;
|
|
using conv_base<T>::stats;
|
|
|
|
using conv_base<T>::RunFind2_0;
|
|
using conv_base<T>::MakeConvProblem;
|
|
|
|
static constexpr const bool is_conv_wrw_f32 = std::is_same<T, float>::value;
|
|
|
|
verify_backward_weights_conv(const tensor<T>& pinput,
|
|
const tensor<T>& pweights,
|
|
const tensor<T>& pout,
|
|
const miopen::ConvolutionDescriptor& pfilter,
|
|
conv_stats& pstats,
|
|
bool preallocate_,
|
|
int pbias,
|
|
int psearch)
|
|
: conv_base<T>(preallocate_)
|
|
{
|
|
input = pinput;
|
|
weights = pweights;
|
|
out = pout;
|
|
filter = pfilter;
|
|
bias = pbias;
|
|
search = psearch;
|
|
stats = &pstats;
|
|
}
|
|
|
|
tensor<T> cpu() const
|
|
{
|
|
auto rweights = weights;
|
|
std::fill(rweights.begin(), rweights.end(), 0);
|
|
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_wrw(out, rweights, input, filter);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_weight(filter.GetSpatialDimension(),
|
|
out,
|
|
rweights,
|
|
input,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_wrw(input, rweights, out, filter);
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference not run");
|
|
cpu_convolution_backward_weight(filter.GetSpatialDimension(),
|
|
input,
|
|
rweights,
|
|
out,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
}
|
|
return rweights;
|
|
}
|
|
|
|
tensor<T> gpu()
|
|
{
|
|
auto&& handle = get_handle();
|
|
auto rweights = weights;
|
|
std::fill(rweights.begin(), rweights.end(), 0);
|
|
|
|
auto out_dev = handle.Write(out.data);
|
|
auto wei_dev = handle.Write(rweights.data);
|
|
auto in_dev = handle.Write(input.data);
|
|
Workspace wspace{};
|
|
|
|
miopenConvSolution_t selected;
|
|
auto fallback_path_taken = miopen::FallbackPath();
|
|
std::size_t count = 0;
|
|
|
|
const auto ctx = ExecutionContext{&handle};
|
|
const auto problem =
|
|
ConvProblemDescription{filter.mode != miopenTranspose ? out.desc : input.desc,
|
|
rweights.desc,
|
|
filter.mode != miopenTranspose ? input.desc : out.desc,
|
|
filter,
|
|
Direction::BackwardWeights};
|
|
|
|
switch(api)
|
|
{
|
|
case ConvApi::Immediate: {
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
filter.FindConvBwdWeightsAlgorithm(
|
|
handle,
|
|
filter.mode == miopenTranspose ? input.desc : out.desc,
|
|
filter.mode == miopenTranspose ? in_dev.get() : out_dev.get(),
|
|
filter.mode == miopenTranspose ? out.desc : input.desc,
|
|
filter.mode == miopenTranspose ? out_dev.get() : in_dev.get(),
|
|
rweights.desc,
|
|
wei_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
|
|
count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount." << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
// std::cout << "Backward weights conv solutions available: " << count << std::endl;
|
|
auto solutions = filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
std::sort(solutions.begin(), solutions.end(), [](const auto& l, const auto& r) {
|
|
return l.time < r.time;
|
|
});
|
|
selected = std::move(solutions.front());
|
|
|
|
[[maybe_unused]] std::size_t ws_size = filter.GetWrwSolutionWorkspaceSize(
|
|
handle,
|
|
filter.mode == miopenTranspose ? input.desc : out.desc,
|
|
filter.mode == miopenTranspose ? out.desc : input.desc,
|
|
rweights.desc,
|
|
selected.solution_id);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
if(selected.workspace_size > 0)
|
|
{
|
|
wspace.resize(selected.workspace_size);
|
|
}
|
|
|
|
filter.ConvolutionWrwImmediate(
|
|
handle,
|
|
filter.mode == miopenTranspose ? input.desc : out.desc,
|
|
filter.mode == miopenTranspose ? in_dev.get() : out_dev.get(),
|
|
filter.mode == miopenTranspose ? out.desc : input.desc,
|
|
filter.mode == miopenTranspose ? out_dev.get() : in_dev.get(),
|
|
rweights.desc,
|
|
wei_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
break;
|
|
}
|
|
case ConvApi::Find_1_0: {
|
|
|
|
wspace.resize(filter.GetWorkSpaceSize(ctx, problem));
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
float alpha = 1, beta = 0;
|
|
filter.FindConvBwdWeightsAlgorithm(
|
|
handle,
|
|
filter.mode == miopenTranspose ? input.desc : out.desc,
|
|
filter.mode == miopenTranspose ? in_dev.get() : out_dev.get(),
|
|
filter.mode == miopenTranspose ? out.desc : input.desc,
|
|
filter.mode == miopenTranspose ? out_dev.get() : in_dev.get(),
|
|
rweights.desc,
|
|
wei_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
|
|
if(perf.memory > 0)
|
|
{
|
|
wspace.resize(perf.memory);
|
|
}
|
|
|
|
filter.ConvolutionBackwardWeights(
|
|
handle,
|
|
&alpha,
|
|
filter.mode == miopenTranspose ? input.desc : out.desc,
|
|
filter.mode == miopenTranspose ? in_dev.get() : out_dev.get(),
|
|
filter.mode == miopenTranspose ? out.desc : input.desc,
|
|
filter.mode == miopenTranspose ? out_dev.get() : in_dev.get(),
|
|
perf.bwd_weights_algo,
|
|
&beta,
|
|
rweights.desc,
|
|
wei_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size());
|
|
|
|
/// \ref read_solver_name
|
|
const auto solutions = filter.GetSolutions(ctx, problem, 1, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count > 0)
|
|
selected = std::move(solutions.front());
|
|
|
|
break;
|
|
}
|
|
case ConvApi::Find_2_0: {
|
|
const auto f2_problem = MakeConvProblem(
|
|
miopenProblemDirectionBackwardWeights, &input.desc, &rweights.desc, &out.desc);
|
|
|
|
const miopenTensorArgument_t arguments[3] = {
|
|
{miopenTensorConvolutionX, nullptr, in_dev.get()},
|
|
{miopenTensorConvolutionW, nullptr, wei_dev.get()},
|
|
{miopenTensorConvolutionY, nullptr, out_dev.get()},
|
|
};
|
|
|
|
RunFind2_0(f2_problem.get(), arguments);
|
|
break;
|
|
}
|
|
case ConvApi::Invalid: MIOPEN_THROW(miopenStatusInvalidValue);
|
|
}
|
|
|
|
if(count != 0)
|
|
{
|
|
stats->algorithm = selected.algorithm;
|
|
stats->solver_name = miopen::solver::Id(selected.solution_id).ToString();
|
|
if(fallback_path_taken != miopen::FallbackPath::None)
|
|
stats->solver_name += "_fallback";
|
|
}
|
|
rweights.data = handle.Read<T>(wei_dev, rweights.data.size());
|
|
return rweights;
|
|
}
|
|
|
|
void fail(float) const
|
|
{
|
|
std::cout << "Backward weights convolution: " << stats->solver_name << std::endl;
|
|
this->conv_base<T>::fail();
|
|
}
|
|
};
|
|
template <class T>
|
|
struct verify_forward_conv_int8 : conv_base<T>
|
|
{
|
|
using conv_base<T>::input;
|
|
using conv_base<T>::weights;
|
|
using conv_base<T>::filter;
|
|
using conv_base<T>::bias;
|
|
using conv_base<T>::search;
|
|
using conv_base<T>::stats;
|
|
bool is_vect;
|
|
|
|
verify_forward_conv_int8(const tensor<T>& pinput,
|
|
const tensor<T>& pweights,
|
|
const miopen::ConvolutionDescriptor& pfilter,
|
|
conv_stats& pstats,
|
|
int pbias = 0,
|
|
int psearch = 0,
|
|
bool pvect = false)
|
|
{
|
|
input = pinput;
|
|
weights = pweights;
|
|
filter = pfilter;
|
|
bias = pbias;
|
|
search = psearch;
|
|
is_vect = pvect;
|
|
stats = &pstats;
|
|
}
|
|
|
|
tensor<float> cpu() const
|
|
{
|
|
auto rout = get_output_tensor_int8(filter, input, weights);
|
|
|
|
if(filter.mode == miopenConvolution)
|
|
{
|
|
bool gpu_ref_used = gpu_ref_convolution_fwd(input, weights, rout, filter);
|
|
|
|
if(!gpu_ref_used)
|
|
{
|
|
MIOPEN_LOG_W("GPU reference skipped");
|
|
cpu_convolution_forward(filter.GetSpatialDimension(),
|
|
input,
|
|
weights,
|
|
rout,
|
|
filter.GetConvPads(),
|
|
filter.GetConvStrides(),
|
|
filter.GetConvDilations(),
|
|
filter.GetGroupCount());
|
|
}
|
|
|
|
rout.par_for_each(
|
|
[&](auto... is) { rout(is...) = double(rout(is...)) + double(this->bias); });
|
|
}
|
|
|
|
return rout;
|
|
}
|
|
|
|
tensor<float> gpu() const
|
|
{
|
|
auto&& handle = get_handle();
|
|
auto rout = get_output_tensor_int8(filter, input, weights);
|
|
|
|
auto in_dev = handle.Write(input.data);
|
|
auto wei_dev = handle.Write(weights.data);
|
|
auto out_dev = handle.Write(rout.data);
|
|
|
|
bool is_transform = (input.desc.GetLengths()[1] % 4 != 0 || is_vect);
|
|
|
|
std::vector<std::size_t> in_len(input.desc.GetLengths().begin(),
|
|
input.desc.GetLengths().end());
|
|
std::vector<std::size_t> wei_len(weights.desc.GetLengths().begin(),
|
|
weights.desc.GetLengths().end());
|
|
in_len[1] = ((in_len[1] + 3) / 4) * 4;
|
|
wei_len[1] = ((wei_len[1] + 3) / 4) * 4;
|
|
|
|
miopen::TensorDescriptor input_vpad_desc(miopenInt8, in_len);
|
|
miopen::TensorDescriptor weight_vpad_desc(miopenInt8, wei_len);
|
|
|
|
auto input_vpad = tensor<T>{in_len};
|
|
auto weights_vpad = tensor<T>{wei_len};
|
|
auto in_vpad_dev = handle.Write(input_vpad.data);
|
|
auto wei_vpad_dev = handle.Write(weights_vpad.data);
|
|
|
|
const auto ctx = ExecutionContext{&handle};
|
|
const auto problem = ConvProblemDescription{
|
|
is_transform ? weight_vpad_desc : weights.desc,
|
|
is_transform ? input_vpad_desc : input.desc,
|
|
rout.desc,
|
|
filter,
|
|
Direction::Forward,
|
|
};
|
|
|
|
if(is_transform)
|
|
{
|
|
float aph = 1.0;
|
|
float bta = 0.0;
|
|
miopen::TransformTensor(
|
|
handle, &aph, input.desc, in_dev.get(), &bta, input_vpad_desc, in_vpad_dev.get());
|
|
|
|
miopen::TransformTensor(handle,
|
|
&aph,
|
|
weights.desc,
|
|
wei_dev.get(),
|
|
&bta,
|
|
weight_vpad_desc,
|
|
wei_vpad_dev.get());
|
|
}
|
|
|
|
Workspace wspace{filter.GetWorkSpaceSize(ctx, problem)};
|
|
|
|
int ret_algo_count;
|
|
miopenConvAlgoPerf_t perf;
|
|
|
|
if(miopen::debug::testing_find_db_enabled)
|
|
{
|
|
filter.FindConvFwdAlgorithm(handle,
|
|
(is_transform ? input_vpad_desc : input.desc),
|
|
(is_transform ? in_vpad_dev.get() : in_dev.get()),
|
|
(is_transform ? weight_vpad_desc : weights.desc),
|
|
(is_transform ? wei_vpad_dev.get() : wei_dev.get()),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
1,
|
|
&ret_algo_count,
|
|
&perf,
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
search);
|
|
}
|
|
|
|
auto count = filter.GetSolutionCount(ctx, problem);
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Using immediate mode error in GetSolutionCount." << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
// std::cout << "Forward Conv solutions available: " << count << std::endl;
|
|
auto fallback_path_taken = miopen::FallbackPath();
|
|
auto solutions = filter.GetSolutions(ctx, problem, count, &fallback_path_taken);
|
|
count = solutions.size();
|
|
|
|
if(count == 0)
|
|
{
|
|
std::cout << "FAILED: Immediate mode has no fallback for this configuration."
|
|
<< " Solution count: " << count << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
std::sort(solutions.begin(), solutions.end(), [](const auto& l, const auto& r) {
|
|
return l.time < r.time;
|
|
});
|
|
auto selected = std::move(solutions.front());
|
|
|
|
[[maybe_unused]] std::size_t ws_size =
|
|
filter.GetForwardSolutionWorkspaceSize(handle,
|
|
(is_transform ? weight_vpad_desc : weights.desc),
|
|
(is_transform ? input_vpad_desc : input.desc),
|
|
rout.desc,
|
|
selected.solution_id);
|
|
|
|
filter.CompileSolution(ctx, problem, selected.solution_id);
|
|
|
|
if(selected.workspace_size > 0)
|
|
{
|
|
wspace.resize(selected.workspace_size);
|
|
}
|
|
|
|
filter.ConvolutionForwardImmediate(handle,
|
|
(is_transform ? weight_vpad_desc : weights.desc),
|
|
(is_transform ? wei_vpad_dev.get() : wei_dev.get()),
|
|
(is_transform ? input_vpad_desc : input.desc),
|
|
(is_transform ? in_vpad_dev.get() : in_dev.get()),
|
|
rout.desc,
|
|
out_dev.get(),
|
|
wspace.ptr(),
|
|
wspace.size(),
|
|
selected.solution_id);
|
|
|
|
if(count != 0)
|
|
{
|
|
stats->algorithm = selected.algorithm;
|
|
stats->solver_name = miopen::solver::Id(selected.solution_id).ToString();
|
|
if(fallback_path_taken != miopen::FallbackPath::None)
|
|
stats->solver_name += "_fallback";
|
|
}
|
|
rout.data = handle.Read<float>(out_dev, rout.data.size());
|
|
|
|
return rout;
|
|
}
|
|
|
|
void fail(float = 0) const
|
|
{
|
|
std::cout << "Forward convolution: " << stats->solver_name << std::endl;
|
|
this->conv_base<T>::fail();
|
|
}
|
|
};
|
|
|
|
template <class T, ConvApi api = ConvApi::Find_1_0, class Tout = T>
|
|
struct conv_driver : test_driver
|
|
{
|
|
tensor<T> input;
|
|
tensor<T> weights;
|
|
miopen::ConvolutionDescriptor filter;
|
|
std::string conv_mode;
|
|
std::string pad_mode;
|
|
std::vector<std::size_t> spatial_dim_elements{};
|
|
std::vector<std::size_t> input_dims{};
|
|
std::vector<std::size_t> weight_tensor_dims{};
|
|
std::vector<std::size_t> filter_dims{};
|
|
std::size_t batch_size{};
|
|
std::size_t input_channels{};
|
|
std::size_t output_channels{};
|
|
// Default to 1 for non-vectorized tensors (0 causes stride calculation to fail)
|
|
std::size_t vector_length{1};
|
|
std::size_t tensor_vect{}; // 0: non vectorized, 1: C-vectorized, 2: N-vectorized. keep same
|
|
// as MIOpenDriver InputFlag "tensor_vect"
|
|
std::string in_layout;
|
|
std::string fil_layout; // keep same as MIOpenDriver argument name
|
|
std::string out_layout;
|
|
std::vector<int> pads_strides_dilations;
|
|
std::vector<int> trans_output_pads;
|
|
int groupCount{};
|
|
bool do_forward = true;
|
|
bool do_backward_data = true;
|
|
bool do_backward_weights = true;
|
|
int search = 0;
|
|
bool gen_float = false;
|
|
bool enable_fdb = true;
|
|
std::string output_type = "";
|
|
bool int8_vectorize = false;
|
|
bool deterministic = false;
|
|
bool preallocate = false;
|
|
|
|
std::unordered_map<std::string, miopenConvolutionMode_t> cmode_lookup = {
|
|
{"CONV", miopenConvolution},
|
|
{"TRANS", miopenTranspose},
|
|
{"CONVOLUTION", miopenConvolution},
|
|
{"TRANSPOSE", miopenTranspose}};
|
|
|
|
std::unordered_map<std::string, miopenPaddingMode_t> pmode_lookup = {
|
|
{"SAME", miopenPaddingSame},
|
|
{"VALID", miopenPaddingValid},
|
|
{"DEFAULT", miopenPaddingDefault}};
|
|
|
|
std::vector<std::size_t> get_batch_sizes() { return {1, 8, 2, 64, 30, 128, 352, 512}; }
|
|
|
|
std::vector<std::vector<std::size_t>> get_2d_spatial_dims()
|
|
{
|
|
return {{14, 14},
|
|
{28, 28},
|
|
{32, 32},
|
|
{7, 7},
|
|
{17, 17},
|
|
{56, 56},
|
|
{55, 55},
|
|
{64, 128},
|
|
{224, 224},
|
|
{1024, 2048},
|
|
{3072, 3072},
|
|
{1, 1},
|
|
{1, 7},
|
|
{7, 1}};
|
|
}
|
|
|
|
std::vector<std::vector<std::size_t>> get_2d_filter_dims()
|
|
{
|
|
return {{1, 1}, {3, 3}, {1, 7}, {5, 5}, {7, 1}, {7, 7}, {11, 11}, {2, 2}, {4, 4}};
|
|
}
|
|
|
|
std::vector<std::size_t> get_output_channels()
|
|
{
|
|
return {32, 64, 16, 128, 96, 112, 192, 256, 320, 512, 1024};
|
|
}
|
|
|
|
std::vector<std::size_t> get_input_channels()
|
|
{
|
|
return {16, 32, 3, 128, 96, 112, 192, 256, 320, 512, 1024};
|
|
}
|
|
|
|
std::vector<std::vector<int>> get_2d_pads_strides_dilations()
|
|
{
|
|
return {{0, 0, 1, 1, 1, 1},
|
|
{0, 0, 2, 2, 1, 1},
|
|
{1, 1, 1, 1, 1, 1},
|
|
{1, 1, 2, 2, 1, 1},
|
|
{2, 2, 1, 1, 1, 1},
|
|
{3, 3, 2, 2, 1, 1},
|
|
{0, 0, 1, 1, 2, 2},
|
|
{1, 1, 2, 2, 3, 3},
|
|
{3, 3, 2, 2, 4, 4},
|
|
{0, 0, 1, 1, 1, 2},
|
|
{1, 1, 2, 2, 2, 1}};
|
|
}
|
|
|
|
std::vector<std::vector<std::size_t>> get_3d_spatial_dims()
|
|
{
|
|
return {{3, 4, 4},
|
|
{4, 9, 9},
|
|
{3, 14, 14},
|
|
{4, 28, 28},
|
|
{4, 56, 56},
|
|
{4, 161, 700},
|
|
{4, 227, 227},
|
|
{1, 1, 1},
|
|
{1, 2, 2}};
|
|
}
|
|
|
|
std::vector<std::vector<std::size_t>> get_3d_filter_dims()
|
|
{
|
|
return {{1, 1, 1},
|
|
{3, 3, 3},
|
|
{3, 5, 5},
|
|
{3, 7, 7},
|
|
{5, 7, 7},
|
|
{3, 11, 11},
|
|
{3, 1, 7},
|
|
{3, 7, 1},
|
|
{3, 5, 20}};
|
|
}
|
|
|
|
std::vector<std::vector<int>> get_2d_trans_output_pads() { return {{0, 0}}; }
|
|
|
|
std::vector<std::vector<int>> get_3d_pads_strides_dilations()
|
|
{
|
|
return {{0, 0, 0, 1, 1, 1, 1, 1, 1},
|
|
{0, 0, 0, 2, 2, 2, 1, 1, 1},
|
|
{1, 1, 1, 1, 1, 1, 1, 1, 1},
|
|
{1, 1, 1, 2, 2, 2, 1, 1, 1},
|
|
{2, 2, 2, 1, 1, 1, 1, 1, 1},
|
|
{3, 3, 3, 2, 2, 2, 1, 1, 1},
|
|
{0, 0, 0, 1, 1, 1, 2, 2, 2},
|
|
{1, 1, 0, 2, 2, 2, 3, 3, 3},
|
|
{3, 3, 3, 2, 2, 2, 4, 4, 4},
|
|
{0, 0, 0, 1, 1, 1, 1, 1, 2},
|
|
{1, 1, 1, 2, 2, 2, 2, 2, 1},
|
|
{2, 2, 2, 1, 1, 1, 4, 4, 3},
|
|
{3, 3, 3, 2, 2, 2, 3, 3, 4}};
|
|
}
|
|
|
|
std::vector<std::vector<int>> get_3d_trans_output_pads() { return {{0, 0, 0}}; }
|
|
|
|
int get_spatial_dim() const
|
|
{
|
|
for(int i = 2; i < 4; i++)
|
|
{
|
|
if(input_dims.size() == i + 2 and weight_tensor_dims.size() == i + 2 and
|
|
pads_strides_dilations.size() == i * 3ULL and trans_output_pads.size() == i)
|
|
return i;
|
|
}
|
|
std::cout << "FAILED: get_spatial_dim() can't calculate dims count." << std::endl;
|
|
exit(-1); // NOLINT (concurrency-mt-unsafe)
|
|
}
|
|
|
|
conv_driver()
|
|
{
|
|
add(conv_mode, "cmode", generate_data({"conv"}));
|
|
add(pad_mode, "pmode", generate_data({"default", "same", "valid"}));
|
|
add(groupCount, "group-count", generate_data({1}));
|
|
add(do_forward, "disable-forward", set_value(false));
|
|
add(do_backward_data, "disable-backward-data", set_value(false));
|
|
add(do_backward_weights, "disable-backward-weights", set_value(false));
|
|
add(search, "search", set_value(1));
|
|
add(gen_float, "generate-float", set_value(true));
|
|
|
|
if constexpr(api == ConvApi::Immediate)
|
|
add(enable_fdb, "enable-fdb", generate_data({false, true}));
|
|
else if constexpr(api == ConvApi::Find_2_0)
|
|
add(preallocate, "preallocate", generate_data({false, true}));
|
|
}
|
|
|
|
void run()
|
|
{
|
|
if(!input_dims.empty())
|
|
filter.spatialDim = get_spatial_dim();
|
|
else
|
|
filter.spatialDim = filter_dims.size();
|
|
bool is_int8 = (input.desc.GetType() == miopenInt8);
|
|
|
|
filter.mode = cmode_lookup[miopen::ToUpper(conv_mode)];
|
|
filter.paddingMode = pmode_lookup[miopen::ToUpper(pad_mode)];
|
|
std::size_t spatial_dim = filter.GetSpatialDimension();
|
|
filter.group_count = std::max(static_cast<int>(groupCount), 1);
|
|
|
|
miopenTensorLayout_t input_layout_t =
|
|
StringToLayoutType(in_layout, tensor_vect, vector_length);
|
|
miopenTensorLayout_t weight_layout_t =
|
|
StringToLayoutType(fil_layout, tensor_vect, vector_length);
|
|
|
|
if(!input_dims.empty())
|
|
{
|
|
input = tensor<T>{input_layout_t, input_dims}.generate(tensor_elem_gen_integer{17});
|
|
batch_size = input_dims.at(0);
|
|
input_channels = input_dims.at(1);
|
|
std::copy(input_dims.begin() + 2, input_dims.end(), spatial_dim_elements.begin());
|
|
}
|
|
else if(spatial_dim == 2)
|
|
{
|
|
///\todo This means input_dims ranged in NCHW way, shall we determine the tensor
|
|
/// dimension via layout string?
|
|
input = tensor<T>{input_layout_t,
|
|
batch_size,
|
|
input_channels,
|
|
spatial_dim_elements.at(0),
|
|
spatial_dim_elements.at(1)}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
else if(spatial_dim == 3)
|
|
{
|
|
input = tensor<T>{batch_size,
|
|
input_channels,
|
|
spatial_dim_elements.at(0),
|
|
spatial_dim_elements.at(1),
|
|
spatial_dim_elements.at(2)}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
|
|
if(!weight_tensor_dims.empty())
|
|
{
|
|
if(fil_layout == "CHWN")
|
|
{
|
|
weights = tensor<T>{weight_layout_t, weight_tensor_dims}.generate(
|
|
tensor_elem_gen_integer{17});
|
|
output_channels = weight_tensor_dims.at(3);
|
|
std::copy(weight_tensor_dims.begin() + 1,
|
|
weight_tensor_dims.end() - 1,
|
|
filter_dims.begin());
|
|
}
|
|
else
|
|
{
|
|
weights = tensor<T>{weight_layout_t, weight_tensor_dims}.generate(
|
|
tensor_elem_gen_integer{17});
|
|
output_channels = weight_tensor_dims.at(0);
|
|
std::copy(
|
|
weight_tensor_dims.begin() + 2, weight_tensor_dims.end(), filter_dims.begin());
|
|
}
|
|
}
|
|
else if(spatial_dim == 2)
|
|
{
|
|
if(fil_layout == "NCHW")
|
|
{
|
|
weights = tensor<T>{weight_layout_t,
|
|
output_channels,
|
|
input_channels / filter.group_count,
|
|
filter_dims.at(0),
|
|
filter_dims.at(1)}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
else if(fil_layout == "CHWN")
|
|
{
|
|
weights = tensor<T>{weight_layout_t,
|
|
input_channels / filter.group_count,
|
|
filter_dims.at(0),
|
|
filter_dims.at(1),
|
|
output_channels}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
else
|
|
{
|
|
weights = tensor<T>{output_channels,
|
|
input_channels / filter.group_count,
|
|
filter_dims.at(0),
|
|
filter_dims.at(1)}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
}
|
|
else if(spatial_dim == 3)
|
|
{
|
|
weights = tensor<T>{output_channels,
|
|
input_channels / filter.group_count,
|
|
filter_dims.at(0),
|
|
filter_dims.at(1),
|
|
filter_dims.at(2)}
|
|
.generate(tensor_elem_gen_integer{17});
|
|
}
|
|
|
|
if(input.desc.GetNumDims() != in_layout.size() ||
|
|
weights.desc.GetNumDims() != fil_layout.size() ||
|
|
input.desc.GetNumDims() != out_layout.size())
|
|
{
|
|
std::cout << input.desc.GetNumDims() << "," << in_layout.size() << std::endl;
|
|
std::cout << weights.desc.GetNumDims() << "," << fil_layout.size() << std::endl;
|
|
std::cout << input.desc.GetNumDims() << "," << out_layout.size() << std::endl;
|
|
std::cerr << "FAILED: layout not match dimension size!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
// reconstruct tensor descriptor(desc) when layout is not the default NCHW layout.
|
|
// by default, this member is constructed when conv2d/3d is constructed (see
|
|
// test_driver::add())
|
|
// but this requires the dimensions come from commandline, which is hard for non-NCHW layout
|
|
if(in_layout != "NCHW" && in_layout != "NCDHW")
|
|
{
|
|
const std::vector<std::size_t> dim_lens = input.desc.GetLengths();
|
|
std::vector<std::size_t> dim_strides;
|
|
miopen::tensor_layout_to_strides(
|
|
dim_lens,
|
|
miopen::tensor_layout_get_default(weights.desc.GetNumDims()),
|
|
in_layout,
|
|
vector_length,
|
|
dim_strides);
|
|
input.desc = miopen::TensorDescriptor(miopen_type<T>{}, dim_lens, dim_strides);
|
|
}
|
|
if(fil_layout != "NCHW" && fil_layout != "NCDHW" && fil_layout != "CHWN")
|
|
{
|
|
const std::vector<std::size_t> dim_lens = weights.desc.GetLengths();
|
|
std::vector<std::size_t> dim_strides;
|
|
miopen::tensor_layout_to_strides(
|
|
dim_lens,
|
|
miopen::tensor_layout_get_default(weights.desc.GetNumDims()),
|
|
fil_layout,
|
|
vector_length,
|
|
dim_strides);
|
|
weights.desc = miopen::TensorDescriptor(miopen_type<T>{}, dim_lens, dim_strides);
|
|
}
|
|
|
|
if(input.desc.GetNumDims() != 2 + spatial_dim ||
|
|
weights.desc.GetNumDims() != 2 + spatial_dim ||
|
|
pads_strides_dilations.size() != 3 * spatial_dim ||
|
|
trans_output_pads.size() != spatial_dim)
|
|
{
|
|
std::cerr << "FAILED: dimension is wrong!" << std::endl;
|
|
return;
|
|
}
|
|
|
|
filter.pads.resize(spatial_dim);
|
|
filter.strides.resize(spatial_dim);
|
|
filter.dilations.resize(spatial_dim);
|
|
filter.trans_output_pads.resize(spatial_dim);
|
|
if(deterministic)
|
|
filter.attribute.Set(MIOPEN_CONVOLUTION_ATTRIB_DETERMINISTIC, 1);
|
|
|
|
std::copy_n(pads_strides_dilations.begin(), spatial_dim, filter.pads.begin());
|
|
std::copy_n(
|
|
pads_strides_dilations.begin() + spatial_dim, spatial_dim, filter.strides.begin());
|
|
std::copy_n(pads_strides_dilations.begin() + 2 * spatial_dim,
|
|
spatial_dim,
|
|
filter.dilations.begin());
|
|
std::copy_n(trans_output_pads.begin(), spatial_dim, filter.trans_output_pads.begin());
|
|
|
|
std::size_t in_c_len = input.desc.GetLengths()[1];
|
|
std::vector<std::size_t> in_spatial_len(input.desc.GetLengths().begin() + 2,
|
|
input.desc.GetLengths().end());
|
|
std::size_t wei_k_len = weights.desc.GetLengths()[0];
|
|
std::size_t wei_c_len = weights.desc.GetLengths()[1];
|
|
std::vector<std::size_t> wei_spatial_len(weights.desc.GetLengths().begin() + 2,
|
|
weights.desc.GetLengths().end());
|
|
if(fil_layout == "CHWN")
|
|
{
|
|
wei_c_len = weights.desc.GetLengths()[0];
|
|
wei_spatial_len[0] = weights.desc.GetLengths()[1];
|
|
wei_spatial_len[1] = weights.desc.GetLengths()[2];
|
|
wei_k_len = weights.desc.GetLengths()[3];
|
|
}
|
|
|
|
// lack of transposeConv or groupConv for int8 type
|
|
if(is_int8 && filter.mode == miopenTranspose)
|
|
{
|
|
show_command();
|
|
std::cout << "MIOpen doesn't support int8 type transpose convolution." << std::endl;
|
|
return;
|
|
}
|
|
|
|
bool is_bfloat16 =
|
|
(input.desc.GetType() == miopenBFloat16 && weights.desc.GetType() == miopenBFloat16);
|
|
|
|
if(is_bfloat16 && !(filter.spatialDim == 2))
|
|
{
|
|
show_command();
|
|
std::cout << "Skipped: bfloat16 is supported for 2D conv only" << std::endl;
|
|
return;
|
|
}
|
|
|
|
if(((filter.mode == miopenTranspose) &&
|
|
((filter.group_count == 1 && in_c_len == wei_k_len) ||
|
|
(filter.group_count > 1 && wei_k_len % filter.group_count == 0))) ||
|
|
((filter.mode == miopenConvolution) &&
|
|
((filter.group_count == 1 && in_c_len == wei_c_len) ||
|
|
(filter.group_count > 1 && in_c_len % wei_c_len == 0))))
|
|
{
|
|
if(filter.mode == miopenConvolution &&
|
|
(miopen::all_of(filter.GetConvDilations(), [](auto v) { return v == 1; }) ||
|
|
miopen::all_of(wei_spatial_len, [](auto v) { return v == 1; })))
|
|
{
|
|
if(filter.paddingMode == miopenPaddingSame)
|
|
{
|
|
if(miopen::any_of(filter.GetConvStrides(), [](auto v) { return v == 0; }))
|
|
{
|
|
show_command();
|
|
std::cout << "Skipped: stride[i] == 0" << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::vector<std::size_t> pads_(spatial_dim);
|
|
std::vector<std::ptrdiff_t> out_spatial_len(spatial_dim);
|
|
|
|
for(std::size_t i = 0; i < spatial_dim; ++i)
|
|
{
|
|
pads_[i] =
|
|
(in_spatial_len[i] % filter.GetConvStrides()[i] == 0)
|
|
? (std::max(
|
|
static_cast<std::ptrdiff_t>(wei_spatial_len[i]) -
|
|
static_cast<std::ptrdiff_t>(filter.GetConvStrides()[i]),
|
|
static_cast<std::ptrdiff_t>(0)))
|
|
: (std::max(static_cast<std::ptrdiff_t>(wei_spatial_len[i]) -
|
|
static_cast<std::ptrdiff_t>(
|
|
in_spatial_len[i] % filter.GetConvStrides()[i]),
|
|
static_cast<std::ptrdiff_t>(0)));
|
|
|
|
filter.pads[i] = pads_[i] / 2;
|
|
|
|
out_spatial_len[i] = miopen::integer_division_ceil(
|
|
in_spatial_len[i], filter.GetConvStrides()[i]);
|
|
}
|
|
|
|
if(miopen::any_of(out_spatial_len, [](auto v) { return v <= 0; }))
|
|
{
|
|
show_command();
|
|
std::cout << "Skipped: out_spatial_len[i] <= 0" << std::endl;
|
|
return;
|
|
}
|
|
}
|
|
else if(filter.paddingMode == miopenPaddingValid)
|
|
{
|
|
if(miopen::any_of(filter.GetConvStrides(), [](auto v) { return v == 0; }))
|
|
{
|
|
show_command();
|
|
std::cout << "Skipped: stride[i] == 0" << std::endl;
|
|
return;
|
|
}
|
|
|
|
std::vector<ptrdiff_t> out_spatial_len(spatial_dim);
|
|
|
|
for(std::size_t i = 0; i < spatial_dim; ++i)
|
|
{
|
|
filter.pads[i] = 0;
|
|
|
|
out_spatial_len[i] = miopen::integer_division_ceil(
|
|
static_cast<std::ptrdiff_t>(in_spatial_len[i]) -
|
|
static_cast<std::ptrdiff_t>(wei_spatial_len[i]) + 1,
|
|
filter.GetConvStrides()[i]);
|
|
}
|
|
|
|
if(miopen::any_of(out_spatial_len, [](auto v) { return v <= 0; }))
|
|
{
|
|
show_command();
|
|
std::cout << "Skipped: out_spatial_len[i] <= 0" << std::endl;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if(filter.mode == miopenTranspose)
|
|
{
|
|
for(std::size_t i = 0; i < spatial_dim; ++i)
|
|
{
|
|
filter.pads[i] = filter.GetConvStrides()[i] - 1;
|
|
}
|
|
}
|
|
|
|
if(((filter.mode == miopenTranspose) &&
|
|
((filter.group_count == 1 &&
|
|
(input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(0))) ||
|
|
(filter.group_count > 1 &&
|
|
(weights.desc.GetLengths().at(0) % filter.group_count == 0)))) ||
|
|
((filter.mode == miopenConvolution) &&
|
|
((weights.desc.GetLayout_str() == "NCHW") ||
|
|
(weights.desc.GetLayout_str() == "NCHWc") ||
|
|
(weights.desc.GetLayout_str() == "NHWC") ||
|
|
(weights.desc.GetLayout_str() == "NCDHW") ||
|
|
(weights.desc.GetLayout_str() == "NDHWC")) &&
|
|
((filter.group_count == 1 &&
|
|
(input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(1))) ||
|
|
(filter.group_count > 1 &&
|
|
(input.desc.GetLengths().at(1) % weights.desc.GetLengths().at(1) == 0)))) ||
|
|
((filter.mode == miopenConvolution) && (weights.desc.GetLayout_str() == "CHWNc") &&
|
|
((filter.group_count == 1 &&
|
|
(input.desc.GetLengths().at(1) == weights.desc.GetLengths().at(0))) ||
|
|
(filter.group_count > 1 &&
|
|
(input.desc.GetLengths().at(1) % weights.desc.GetLengths().at(0) == 0)))))
|
|
{
|
|
auto output = get_output_tensor<T, Tout>(filter, input, weights, out_layout);
|
|
|
|
auto gen_positive_value = [=, this](auto...) {
|
|
auto data_type = input.desc.GetType();
|
|
int v_max = is_int8 ? 16 : (data_type == miopenHalf) ? 4 : 17;
|
|
return gen_float ? prng::gen_canonical<double>()
|
|
: static_cast<double>(prng::gen_A_to_B(1, v_max));
|
|
};
|
|
|
|
auto gen_sign_value = [=, this](auto... is) {
|
|
auto data_type = input.desc.GetType();
|
|
int v_max = is_int8 ? 16 : (data_type == miopenHalf) ? 4 : 17;
|
|
return gen_float ? prng::gen_A_to_B(-1.0, 1.0)
|
|
: static_cast<double>(prng::gen_A_to_B(1, v_max)) *
|
|
tensor_elem_gen_checkboard_sign{}(is...);
|
|
};
|
|
|
|
auto ctx = miopen::ExecutionContext{&get_handle()};
|
|
|
|
bool skip_forward = false;
|
|
|
|
bool skip_backward_data = is_int8;
|
|
bool skip_backward_weights = is_int8;
|
|
|
|
#if TEST_DIRECT_SUPPORTED_CONFIG_ONLY
|
|
if(input.desc.GetType() == miopenInt8 || input.desc.GetType() == miopenBFloat16)
|
|
{
|
|
show_command();
|
|
std::cout << "Direct path doesn't support Int8 or BFloat16 type." << std::endl;
|
|
return;
|
|
}
|
|
if(input.desc.GetType() == miopenHalf && filter.mode == miopenConvolution)
|
|
{
|
|
skip_forward = !is_direct_fwd_bwd_data_supported(
|
|
get_handle(), filter, input.desc, weights.desc, output.desc);
|
|
|
|
skip_backward_data = skip_forward;
|
|
|
|
skip_backward_weights = !is_direct_bwd_wrw_supported(
|
|
get_handle(), filter, input.desc, weights.desc, output.desc);
|
|
}
|
|
#endif
|
|
|
|
input.generate(gen_positive_value);
|
|
output.generate(gen_positive_value);
|
|
weights.generate(gen_sign_value);
|
|
|
|
size_t total_mem;
|
|
if(is_int8)
|
|
{
|
|
/// \todo Properly construct the `output` tensor descriptor
|
|
/// and get rid of this special "int8" stuff.
|
|
auto output_int8 =
|
|
get_output_tensor<T, Tout>(filter, input, weights, out_layout);
|
|
const auto problem = ConvProblemDescription{input.desc,
|
|
weights.desc,
|
|
std::move(output_int8.desc),
|
|
filter,
|
|
Direction::Forward};
|
|
const auto workspace_size = filter.GetWorkSpaceSize(ctx, problem);
|
|
|
|
// 4x because assume type is miopenInt8x4
|
|
total_mem = input.desc.GetNumBytes() + 4 * input.desc.GetNumBytes() +
|
|
weights.desc.GetNumBytes() + 4 * weights.desc.GetNumBytes() +
|
|
output_int8.desc.GetNumBytes() + 4 * sizeof(char) * workspace_size;
|
|
}
|
|
else
|
|
{
|
|
/// \todo Take into account `skip_forward`, `skip_backward_data`,
|
|
/// `skip_backward_weights` and use this path to compute `total_mem` for int8
|
|
/// variations.
|
|
const auto fwd_problem = miopen::conv::ProblemDescription{
|
|
input.desc,
|
|
weights.desc,
|
|
output.desc,
|
|
filter,
|
|
filter.mode != miopenTranspose ? Direction::Forward
|
|
: Direction::BackwardData};
|
|
|
|
const auto bwd_problem = miopen::conv::ProblemDescription{
|
|
output.desc,
|
|
weights.desc,
|
|
input.desc,
|
|
filter,
|
|
filter.mode != miopenTranspose ? Direction::BackwardData
|
|
: Direction::Forward};
|
|
|
|
const auto wrw_problem = miopen::conv::ProblemDescription{
|
|
filter.mode != miopenTranspose ? output.desc : input.desc,
|
|
weights.desc,
|
|
filter.mode != miopenTranspose ? input.desc : output.desc,
|
|
filter,
|
|
Direction::BackwardWeights};
|
|
|
|
const auto workspaces = std::array<std::size_t, 3>{
|
|
filter.GetWorkSpaceSize(ctx, fwd_problem),
|
|
filter.GetWorkSpaceSize(ctx, bwd_problem),
|
|
filter.GetWorkSpaceSize(ctx, wrw_problem),
|
|
};
|
|
|
|
const auto workspace_size =
|
|
*std::max_element(workspaces.begin(), workspaces.end());
|
|
|
|
total_mem = input.desc.GetNumBytes() + weights.desc.GetNumBytes() +
|
|
output.desc.GetNumBytes() +
|
|
sizeof(char) * workspace_size; // estimate based on backward pass
|
|
}
|
|
|
|
size_t device_mem = get_handle().GetGlobalMemorySize();
|
|
|
|
if(total_mem >= device_mem)
|
|
{
|
|
show_command();
|
|
std::cout << "Config requires " << total_mem
|
|
<< " Bytes to write all necessary tensors to GPU. GPU has "
|
|
<< device_mem << " Bytes of memory." << std::endl;
|
|
return;
|
|
}
|
|
|
|
if(api == ConvApi::Immediate)
|
|
{
|
|
miopen::debug::testing_find_db_enabled = enable_fdb;
|
|
}
|
|
|
|
conv_stats stats;
|
|
|
|
if(do_forward && !skip_forward)
|
|
{
|
|
if(is_int8)
|
|
{
|
|
if(output_type == "float")
|
|
{
|
|
verify(verify_forward_conv<api, T, float>{
|
|
input,
|
|
weights,
|
|
get_output_tensor<T, float>(filter, input, weights, out_layout),
|
|
filter,
|
|
stats,
|
|
preallocate,
|
|
0,
|
|
search,
|
|
int8_vectorize});
|
|
}
|
|
else if(output_type == "int32")
|
|
{
|
|
verify(verify_forward_conv<api, T, int>{
|
|
input,
|
|
weights,
|
|
get_output_tensor<T, int>(filter, input, weights, out_layout),
|
|
filter,
|
|
stats,
|
|
preallocate,
|
|
0,
|
|
search,
|
|
int8_vectorize});
|
|
}
|
|
else if(output_type == "int8")
|
|
{
|
|
verify(verify_forward_conv<api, T, int8_t>{
|
|
input,
|
|
weights,
|
|
get_output_tensor<T, int8_t>(filter, input, weights, out_layout),
|
|
filter,
|
|
stats,
|
|
preallocate,
|
|
0,
|
|
search,
|
|
int8_vectorize});
|
|
}
|
|
else
|
|
{
|
|
show_command();
|
|
std::cout << "FAILED: bad output_type: '" << output_type << '\''
|
|
<< std::endl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
verify(verify_forward_conv<api, T>{
|
|
input, weights, output, filter, stats, preallocate, 0, search, false});
|
|
std::cout << "PASSED: Fwd " << input.desc.ToString() << " -> "
|
|
<< output.desc.ToString() << " [" << stats.solver_name << "]"
|
|
<< std::endl;
|
|
}
|
|
}
|
|
|
|
if(do_backward_data && !skip_backward_data)
|
|
{
|
|
verify(verify_backward_conv<api, T>{
|
|
input, weights, output, filter, stats, preallocate, 0, search});
|
|
std::cout << "PASSED: Bwd " << output.desc.ToString() << " -> "
|
|
<< input.desc.ToString() << " [" << stats.solver_name << "]"
|
|
<< std::endl;
|
|
}
|
|
|
|
if(do_backward_weights && !skip_backward_weights)
|
|
{
|
|
output.generate(gen_sign_value);
|
|
|
|
verify(verify_backward_weights_conv<api, T>{
|
|
input, weights, output, filter, stats, preallocate, 0, search});
|
|
std::cout << "PASSED: WrW " << input.desc.ToString() << " + "
|
|
<< output.desc.ToString() << " -> " << weights.desc.ToString() << " ["
|
|
<< stats.solver_name << "]" << std::endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
};
|