#include "gpu/intel/conv/jit/v2/planner/bench.hpp"
#include "common/dnnl_thread.hpp"
#include "gpu/intel/conv/jit/v2/debug.hpp"
#include "gpu/intel/conv/jit/v2/plan.hpp"
#include "gpu/intel/conv/jit/v2/plan_registry.hpp"
#include "gpu/intel/conv/jit/v2/planner/model_fit.hpp"
#include "gpu/intel/conv/jit/v2/tensor_utils.hpp"
#include "gpu/intel/ocl/usm_utils.hpp"
#include <algorithm>
#include <cassert>
#include <fstream>
#include <iostream>
#include "oneapi/dnnl/dnnl.hpp"
#include "oneapi/dnnl/dnnl_ocl.hpp"
using namespace dnnl;
#ifndef DNNL_EXPERIMENTAL_PROFILING
extern "C" dnnl_status_t dnnl_reset_profiling(dnnl_stream_t stream);
extern "C" dnnl_status_t dnnl_query_profiling_data(dnnl_stream_t stream,
int32_t data_kind, int *num_entries, uint64_t *data);
#endif
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace conv {
namespace jit {
namespace v2 {
namespace planner {
bench_manager_t::bench_manager_t()
: engine_(engine::kind::gpu, 0)
, stream_(engine_,
static_cast<stream::flags>(
stream_flags::in_order | stream_flags::profiling))
, hw_(make_ir_hw(engine_.get())) {}
bench_manager_t::~bench_manager_t() {
dump_plan_registry();
}
static void fill_mem(stream &strm, const memory &mem) {
auto eng = mem.get_engine();
auto *ptr = mem.get_data_handle();
auto md = mem.get_desc();
size_t size = md.get_size();
uint8_t pattern = 0;
status_t status = impl::gpu::intel::ocl::usm::fill(strm.get(), ptr,
&pattern, sizeof(pattern), size, 0, nullptr, nullptr);
if (status != status::success) throw std::runtime_error("Fill failed");
}
class memory_pool_t {
public:
std::unordered_map<int, memory> get_args(
const std::unordered_map<int, memory::desc> &mds) const {
gpu_assert(is_finalized_);
std::unordered_map<int, memory> ret;
for (auto &kv : mds) {
int id = kv.first;
auto &base_mem = base_mems_.at(id);
auto &md = kv.second;
auto eng = base_mem.get_engine();
gpu_assert(md.get_size() <= base_mem.get_desc().get_size());
auto mem = ocl_interop::make_memory(md, eng,
ocl_interop::memory_kind::usm, base_mem.get_data_handle());
ret.emplace(id, mem);
}
return ret;
}
void reserve(int id, const memory::desc &md) {
size_t &size = arg_sizes_[id];
size = std::max(size, md.get_size());
}
void finalize(stream &strm) {
auto eng = strm.get_engine();
for (auto &kv : arg_sizes_) {
int id = kv.first;
memory::dims dims = {(memory::dim)kv.second};
memory::desc md(dims, memory::data_type::u8, memory::format_tag::a);
auto mem = ocl_interop::make_memory(
md, eng, ocl_interop::memory_kind::usm);
fill_mem(strm, mem);
base_mems_.emplace(id, mem);
}
strm.wait();
is_finalized_ = true;
}
operator bool() const { return !base_mems_.empty(); }
private:
bool is_finalized_ = false;
std::unordered_map<int, size_t> arg_sizes_;
std::unordered_map<int, memory> base_mems_;
};
class bench_task_base_t {
public:
static const int iters = 3;
void init_mem(memory_pool_t &mem_pool) {
for (auto &kv : get_mds()) {
mem_pool.reserve(kv.first, kv.second);
}
}
dnnl_status_t bench_async(stream &strm, const memory_pool_t &mem_pool) {
using namespace dnnl::impl;
auto args = mem_pool.get_args(get_mds());
for (int i = 0; i < iters; i++) {
prim_.execute(strm, args);
}
return status::success;
}
template <typename TaskVectorT>
static dnnl_status_t sync(stream &strm, TaskVectorT &vec) {
strm.wait();
int ntasks = (int)vec.size();
int nentries = 0;
int nkernels = 0;
CHECK(dnnl_query_profiling_data(
strm.get(), profiling_data_kind::time, &nentries, nullptr));
CHECK(dnnl_query_profiling_data(strm.get(),
profiling_data_kind::time_per_kernel, &nkernels, nullptr));
gpu_assert(nentries == ntasks * iters);
std::vector<uint64_t> entries(nentries);
std::vector<uint64_t> kernel_entries;
CHECK(dnnl_query_profiling_data(strm.get(), profiling_data_kind::time,
&nentries, entries.data()));
int kernels_per_entry = ir_utils::safe_div(nkernels, nentries);
if (kernels_per_entry > 1) {
kernel_entries.resize(nkernels);
CHECK(dnnl_query_profiling_data(strm.get(),
profiling_data_kind::time_per_kernel, &nkernels,
kernel_entries.data()));
}
auto get_bench_time = [&](int i, int j) {
int idx = iters * i + j;
if (kernels_per_entry == 1) return bench_time_t(entries[idx]);
int beg = idx * kernels_per_entry;
int end = idx * kernels_per_entry + kernels_per_entry;
return bench_time_t(entries[idx], kernel_entries.begin() + beg,
kernel_entries.begin() + end);
};
for (int i = 0; i < ntasks; i++) {
auto time = get_bench_time(i, 0);
for (int j = 1; j < iters; j++) {
auto j_time = get_bench_time(i, j);
time = time.min(j_time);
}
vec[i].set_time(time);
}
return status::success;
}
const bench_time_t &time() const { return time_; }
void set_time(const bench_time_t &time) { time_ = time; }
protected:
void set_primitive(const primitive &prim) { prim_ = prim; }
private:
std::unordered_map<int, memory::desc> get_mds() const {
auto *pd_ptr
= const_cast<dnnl_primitive_desc_t>(prim_.get_primitive_desc());
primitive_desc_base pd(pd_ptr, true);
std::vector<int> arg_ids = {
DNNL_ARG_DIFF_DST,
DNNL_ARG_DIFF_SRC,
DNNL_ARG_DIFF_WEIGHTS,
DNNL_ARG_DST,
DNNL_ARG_SRC,
DNNL_ARG_WEIGHTS,
};
std::unordered_map<int, memory::desc> ret;
for (int id : arg_ids) {
auto md = pd.query_md(dnnl::query::exec_arg_md, id);
if (md.is_zero()) continue;
ret.emplace(id, md);
}
return ret;
}
primitive prim_;
bench_time_t time_;
};
using problem_t = dnnl::impl::gpu::intel::conv::jit::v2::problem_t;
using kernel_desc_t = dnnl::impl::gpu::intel::conv::jit::v2::kernel_desc_t;
using bench_data_t = dnnl::impl::gpu::intel::conv::jit::v2::bench_data_t;
using bench_time_t = dnnl::impl::gpu::intel::conv::jit::v2::bench_time_t;
using tile_t = dnnl::impl::gpu::intel::jit::tile_t;
namespace pvars = dnnl::impl::gpu::intel::jit::pvars;
std::string c_pd_name(dnnl_primitive_desc_t pd) {
const char *res = nullptr;
dnnl_status_t status
= dnnl_primitive_desc_query(pd, dnnl_query_impl_info_str, 0, &res);
gpu_assert(status == dnnl_success);
return std::string(res);
}
dim_t opp_pad(dim_t i, dim_t o, dim_t k, dim_t s, dim_t p, dim_t d) {
return (o - 1) * s - i + ((k - 1) * (d + 1) + 1) - p;
}
class bench_task_t : public bench_task_base_t {
public:
bench_task_t(const problem_t &prb)
: prb_(prb)
, g(prb.shape()[pvars::g])
, mb(prb.shape()[pvars::mb])
, oc(prb.shape()[pvars::oc])
, ic(prb.shape()[pvars::ic])
, ih(prb.shape()[pvars::ih])
, iw(prb.shape()[pvars::iw])
, oh(prb.shape()[pvars::oh])
, ow(prb.shape()[pvars::ow])
, kh(prb.shape()[pvars::kh])
, kw(prb.shape()[pvars::kw])
, sh(prb.shape()[pvars::sh])
, sw(prb.shape()[pvars::sw])
, ph(prb.shape()[pvars::ph])
, pw(prb.shape()[pvars::pw]) {}
const problem_t &prb() const { return prb_; }
bool init_primitive(engine &eng) {
const std::string v2_impl_name = "jit:ir_v2";
try {
memory::dims src_dims = {mb, g * ic, 1, ih, iw};
memory::dims wei_dims = {g, oc, ic, 1, kh, kw};
memory::dims dst_dims = {mb, g * oc, 1, oh, ow};
memory::dims bias_dims = {g * oc};
memory::dims strides = {1, sh, sw};
memory::dims padding_l = {0, ph, pw};
memory::dims padding_r(3);
padding_r[0] = 0;
padding_r[1] = opp_pad(ih, oh, kh, sh, ph, 0);
padding_r[2] = opp_pad(iw, ow, kw, sw, pw, 0);
switch (prb_.prop()) {
case prop_kind::forward_inference:
case prop_kind::forward_training: {
auto src_md = to_memory_desc(prb_.src_tag(), src_dims);
auto wei_md = to_memory_desc(prb_.wei_tag(), wei_dims);
auto dst_md = to_memory_desc(prb_.dst_tag(), dst_dims);
primitive_attr attr;
auto pd = convolution_forward::primitive_desc(eng,
static_cast<enum prop_kind>(prb_.prop()),
algorithm::convolution_direct, src_md, wei_md,
memory::desc(), dst_md, strides, padding_l,
padding_r, attr);
while (pd.impl_info_str() != v2_impl_name) {
if (!pd.next_impl()) break;
}
if (pd.impl_info_str() != v2_impl_name) {
std::cout << "Error: expected conv_v2." << std::endl;
exit(1);
}
auto prim = convolution_forward(pd);
set_primitive(prim);
return true;
}
case prop_kind::backward_data: {
auto diff_src_md = to_memory_desc(prb_.src_tag(), src_dims);
auto wei_md = to_memory_desc(prb_.wei_tag(), wei_dims);
auto diff_dst_md = to_memory_desc(prb_.dst_tag(), dst_dims);
primitive_attr attr;
dnnl_primitive_desc_t c_pd = nullptr;
auto status
= dnnl_convolution_backward_data_primitive_desc_create(
&c_pd, eng.get(),
alg_kind::convolution_direct,
diff_src_md.get(), wei_md.get(),
diff_dst_md.get(), &strides[0], nullptr,
&padding_l[0], &padding_r[0], nullptr,
attr.get());
if (status != status::success) return false;
while (c_pd_name(c_pd) != v2_impl_name) {
auto status = dnnl_primitive_desc_next_impl(c_pd);
if (status == dnnl_last_impl_reached) break;
gpu_assert(status == dnnl_success);
}
auto pd = convolution_backward_data::primitive_desc(c_pd);
if (pd.impl_info_str() != v2_impl_name) {
std::cout << "Error: expected conv_v2." << std::endl;
exit(1);
}
auto prim = convolution_backward_data(pd);
set_primitive(prim);
return true;
}
case prop_kind::backward_weights: {
auto src_md = to_memory_desc(prb_.src_tag(), src_dims);
auto diff_wei_md = to_memory_desc(prb_.wei_tag(), wei_dims);
auto diff_dst_md = to_memory_desc(prb_.dst_tag(), dst_dims);
memory::desc diff_bias_md;
if (!prb_.bias_type().is_undef()) {
auto tag = make_layout_tag(tensor_kind_t::bias,
"a:" + prb_.bias_type().str());
diff_bias_md = to_memory_desc(tag, bias_dims);
}
primitive_attr attr;
dnnl_primitive_desc_t c_pd = nullptr;
auto status
= dnnl_convolution_backward_weights_primitive_desc_create(
&c_pd, eng.get(),
alg_kind::convolution_direct, src_md.get(),
diff_wei_md.get(), diff_bias_md.get(),
diff_dst_md.get(), &strides[0], nullptr,
&padding_l[0], &padding_r[0], nullptr,
attr.get());
if (status != status::success) return false;
while (c_pd_name(c_pd) != v2_impl_name) {
auto status = dnnl_primitive_desc_next_impl(c_pd);
if (status == dnnl_last_impl_reached) break;
gpu_assert(status == dnnl_success);
}
auto pd = convolution_backward_weights::primitive_desc(
c_pd);
if (pd.impl_info_str() != v2_impl_name) {
std::cout << "Error: expected conv_v2." << std::endl;
exit(1);
}
auto prim = convolution_backward_weights(pd);
set_primitive(prim);
return true;
}
default:
std::cout << "Error: unexpected propagation kind"
<< std::endl;
exit(1);
}
} catch (dnnl::error &e) {
std::cout << "Initialization Exception: " << e.message << "\n";
return false;
}
}
std::string str() const {
ostringstream_t oss;
oss << "g" << g;
oss << "mb" << mb;
oss << "ic" << ic;
oss << "ih" << ih;
oss << "oc" << oc;
oss << "oh" << oh;
oss << "kh" << kh;
if (sh != 1) oss << "sh" << sh;
oss << "ph" << ph;
return oss.str();
}
private:
memory::desc to_memory_desc(
const layout_tag_t &tag, const memory::dims &dims) const {
gpu_assert(tag.raw_tag().ndims() == dims.size());
auto md = utils::make_unique<memory_desc_t>();
md->ndims = tag.raw_tag().ndims();
md->data_type = to_dnnl(tag.type());
md->format_kind = format_kind::blocked;
auto &blk = md->format_desc.blocking;
blk = blocking_desc_t();
dim_t stride = 1;
auto rem_dims = dims;
for (int i = tag.raw_tag().nentries() - 1; i >= 0; i--) {
auto &e = tag.raw_tag().entries()[i];
if (e.is_blocked && e.block != 0) {
blk.inner_idxs[blk.inner_nblks] = e.index();
blk.inner_blks[blk.inner_nblks] = e.block;
rem_dims[e.index()]
= utils::div_up(rem_dims[e.index()], e.block);
blk.inner_nblks++;
stride *= e.block;
} else {
blk.strides[e.index()] = stride;
stride *= rem_dims[e.index()];
}
}
for (int i = 0; i < md->ndims; i++) {
dim_t inner = 1;
for (int j = 0; j < blk.inner_nblks; j++) {
if (blk.inner_idxs[j] == i) inner *= blk.inner_blks[j];
}
md->dims[i] = dims[i];
md->padded_dims[i] = utils::rnd_up(dims[i], inner);
}
std::reverse(blk.inner_idxs, blk.inner_idxs + blk.inner_nblks);
std::reverse(blk.inner_blks, blk.inner_blks + blk.inner_nblks);
return memory::desc(md.release());
}
problem_t prb_;
memory::dim g, mb;
memory::dim oc, ic;
memory::dim ih, iw;
memory::dim oh, ow;
memory::dim kh, kw;
memory::dim sh, sw;
memory::dim ph, pw;
};
dim_t random(dim_t a, dim_t b) {
return a + rand() % (b - a + 1);
}
struct random_dim_t {
dim_t lo = 0;
dim_t hi = 0;
dim_t tile = 0;
random_dim_t(const pvar_t &dim, dim_t _tile) : tile(_tile) {}
random_dim_t with_range(dim_t _lo, dim_t _hi) {
auto ret = *this;
ret.lo = utils::div_up(_lo, tile);
ret.hi = _hi / tile;
return ret;
}
explicit operator bool() const { return lo <= hi; }
bool with_tile() const { return tile > 1; }
dim_t operator()() const {
gpu_assert(*this);
return random(lo, hi) * tile;
}
};
struct random_dim_set_t {
std::vector<random_dim_t> dims;
random_dim_set_t(const random_dim_t &d) {
if (!d) return;
dims.push_back(d);
}
random_dim_set_t operator|(const random_dim_set_t &other) const {
random_dim_set_t ret = *this;
ret.dims.insert(ret.dims.end(), other.dims.begin(), other.dims.end());
return ret;
}
size_t size() const { return dims.size(); }
bool with_tile() const { return dims[0].with_tile(); }
dim_t operator()() const {
dim_t idx = random(0, static_cast<dim_t>(size()) - 1);
return dims[idx]();
}
};
random_dim_set_t operator|(const random_dim_t &a, const random_dim_set_t &b) {
return random_dim_set_t(a) | b;
}
tile_t random_shape(const bench_input_params_t ¶ms, const tile_t &tile) {
auto make_random_dim = [&](const pvar_t &dim, dim_t lo = 0, dim_t hi = 0) {
auto ret = random_dim_t(dim, tile.get(dim, 1));
return ret.with_range(lo, hi);
};
auto make_random_dim_set
= [&](const pvar_t &dim, dim_t s, dim_t m, dim_t l) {
auto d = make_random_dim(dim);
auto d_s = d.with_range(1, s);
auto d_m = d.with_range(s + 1, m);
auto d_l = d.with_range(m + 1, l);
return d_s | d_m | d_l;
};
tile_t s = problem_t::default_shape();
auto g = make_random_dim(pvars::g, 2, 512);
auto mb = make_random_dim_set(pvars::mb, 1, 16, 128);
auto ic = make_random_dim_set(pvars::ic, 64, 512, 2048);
auto oc = make_random_dim_set(pvars::oc, 64, 512, 2048);
auto ow = make_random_dim_set(pvars::ow, 64, 512, 2048);
auto iw = make_random_dim_set(pvars::iw, 64, 512, 2048);
if (params.is_dw) {
s[pvars::g] = g();
s[pvars::mb] = mb();
s[pvars::ic] = 1;
s[pvars::oc] = 1;
s[pvars::iw] = s[pvars::ow] = (ow.with_tile() ? ow() : iw());
} else {
s[pvars::g] = 1;
s[pvars::mb] = mb();
s[pvars::ic] = ic();
s[pvars::oc] = oc();
s[pvars::iw] = s[pvars::ow] = (ow.with_tile() ? ow() : iw());
}
s[pvars::kw] = tile.get(pvars::kw, 1);
s[pvars::pw] = (s[pvars::kw] - 1) / 2;
s[pvars::kh] = tile.get(pvars::kh, 1);
s[pvars::ph] = (s[pvars::kh] - 1) / 2;
for (auto &d : s) {
dim_t value;
if (params.reqs.get_value(d, value)) s[d] = value;
}
return s;
}
double footprint(const layout_tag_t &src, const layout_tag_t &wei,
const layout_tag_t &dst, const tile_t &shape) {
#define GET(name) shape[pvars::name]
double src_elems
= (double)GET(g) * GET(mb) * GET(ic) * GET(id) * GET(ih) * GET(iw);
double wei_elems
= (double)GET(g) * GET(oc) * GET(ic) * GET(kd) * GET(kh) * GET(kw);
double dst_elems
= (double)GET(g) * GET(mb) * GET(oc) * GET(od) * GET(oh) * GET(ow);
#undef GET
double ret = 0;
ret += src_elems * src.type().size();
ret += wei_elems * wei.type().size();
ret += dst_elems * dst.type().size();
return ret;
}
tile_t expand_tile(
prop_kind_t prop, const prb_reqs_t &reqs, const tile_t &_tile) {
tile_t tile = _tile;
for (auto &d : index_dims(prop)) {
dim_t mod = reqs.max_factor(d);
mod = math::lcm(mod, tile.get(d, 1));
if (mod == 1) continue;
tile[d] = mod;
}
return tile;
}
std::vector<problem_t> generate_problems(const bench_input_params_t ¶ms) {
if (params.nprbs == 0) return {};
const double max_ops = 1e10;
const double max_bytes = 100e6;
auto tile = expand_tile(params.prop, params.reqs, params.tile);
srand(static_cast<unsigned>(
ir_utils::get_hash(params.reqs.str()) & 0xFFFFFFFFu));
std::vector<problem_t> ret;
const int max_iters = (1 << 24);
for (int iter = 0; iter < max_iters; iter++) {
auto shape = random_shape(params, tile);
if (problem_t::ops(params.prop, shape) > max_ops) continue;
if (footprint(params.src_tag, params.wei_tag, params.dst_tag, shape)
> max_bytes)
continue;
auto prb = params.problem();
prb.set_shape(shape);
if (!params.reqs.fits(prb.shape())) continue;
ret.push_back(std::move(prb));
if ((int)ret.size() >= params.nprbs) break;
}
if ((int)ret.size() < params.nprbs) {
std::cout << "Could not generate " << params.nprbs << " problems after "
<< max_iters << " iterations" << std::endl;
std::cout << params.reqs << std::endl;
exit(1);
}
return ret;
}
std::vector<problem_t> load_problems(const std::string &path) {
std::vector<problem_t> prbs;
std::ifstream f(path);
std::string line;
while (std::getline(f, line)) {
if (line.empty() || line[0] == '#') continue;
prbs.emplace_back(line);
}
return prbs;
}
bench_data_t bench(const bench_manager_t &bench_mger,
const kernel_desc_t &_kernel_desc, std::vector<bench_task_t> &tasks,
memory_pool_t *mem_pool_ptr = nullptr) {
int ntasks = (int)tasks.size();
auto eng = bench_mger.get_engine();
auto strm = bench_mger.get_stream();
std::cout << "Running benchmark for descriptor: " << _kernel_desc.cmd_str()
<< std::endl;
gpu_assert(!_kernel_desc.spec.is_dynamic());
auto kernel_desc = _kernel_desc;
kernel_desc.spec.mode = specialization_mode_t::_default;
{
auto guard = debug_t::make_kernel_desc_setter(kernel_desc);
if (!tasks[0].init_primitive(eng)) return {};
}
parallel_nd(ntasks, [&](dim_t i) {
auto guard = debug_t::make_kernel_desc_setter(kernel_desc);
bool ok = tasks[i].init_primitive(eng);
if (!ok) throw std::runtime_error("Initialization failed");
});
memory_pool_t _mem_pool;
memory_pool_t &mem_pool = (mem_pool_ptr ? *mem_pool_ptr : _mem_pool);
if (!mem_pool) {
for (auto &t : tasks) {
t.init_mem(mem_pool);
}
mem_pool.finalize(strm);
}
bench_data_t bd(0, _kernel_desc);
status_t status = dnnl_reset_profiling(strm.get());
if (status != status::success)
throw std::runtime_error("Reset profiling failed.");
for (int i = 0; i < ntasks; i++) {
status = tasks[i].bench_async(strm, mem_pool);
if (status != status::success)
throw std::runtime_error("Benchmark failed.");
}
status = bench_task_base_t::sync(strm, tasks);
if (status != status::success) throw std::runtime_error("Sync failed.");
for (int i = 0; i < ntasks; i++) {
bd.add(tasks[i].prb(), tasks[i].time());
}
std::cout << bd << std::endl;
return bd;
}
class bench_runner_impl_t {
public:
bench_runner_impl_t(const bench_manager_t &bench_mger,
const bench_input_params_t ¶ms)
: bench_mger_(bench_mger) {
auto prbs = generate_problems(params);
for (auto &prb : prbs) {
tasks_.emplace_back(prb);
}
}
bench_data_t bench(const kernel_desc_t &kernel_desc) {
if (tasks_.empty()) return bench_data_t();
if (!create_plan(kernel_desc, bench_mger_.hw())) return {};
return planner::bench(bench_mger_, kernel_desc, tasks_, &mem_pool_);
}
private:
const bench_manager_t &bench_mger_;
std::vector<bench_task_t> tasks_;
memory_pool_t mem_pool_;
};
bench_runner_t::bench_runner_t(
const bench_manager_t &bench_mger, const bench_input_params_t ¶ms)
: impl_(std::make_shared<bench_runner_impl_t>(bench_mger, params)) {}
bench_data_t bench_runner_t::bench(const kernel_desc_t &kernel_desc) {
return impl_->bench(kernel_desc);
}
bench_data_t bench(const bench_manager_t &bench_mger,
const kernel_desc_t &kernel_desc, int nprbs) {
if (!create_plan(kernel_desc, bench_mger.hw())) return {};
bench_runner_t runner(bench_mger,
bench_input_params_t(kernel_desc, bench_mger.hw(), nprbs));
return runner.bench(kernel_desc);
}
bool try_create(
const bench_manager_t &bench_mger, const kernel_desc_t &kernel_desc) {
bench_input_params_t params(kernel_desc, bench_mger.hw(), 1);
bench_task_t task(generate_problems(params)[0]);
auto engine = bench_mger.get_engine();
auto guard = debug_t::make_kernel_desc_setter(kernel_desc);
return task.init_primitive(engine);
}
layout_tag_t &get_out_tag(kernel_desc_t &kernel_desc) {
switch (kernel_desc.prop) {
case prop_kind::forward: return kernel_desc.dst_tag;
case prop_kind::backward_data: return kernel_desc.src_tag;
case prop_kind::backward_weights: return kernel_desc.wei_tag;
default: gpu_error_not_expected();
}
return kernel_desc.dst_tag;
}
std::vector<dsl::type_t> get_out_types(const kernel_desc_t &kernel_desc) {
std::vector<dsl::type_t> ret;
switch (kernel_desc.prop) {
case prop_kind::forward:
ret.push_back(dsl::type_t::s8());
ret.push_back(dsl::type_t::f16());
ret.push_back(dsl::type_t::f32());
break;
case prop_kind::backward_data: break;
case prop_kind::backward_weights:
ret.push_back(dsl::type_t::f32());
if (kernel_desc.wei_tag.type().is_bf16())
ret.push_back(dsl::type_t::bf16());
default: break;
}
return ret;
}
kernel_desc_t try_extensions(
const bench_manager_t &bench_mger, const kernel_desc_t &kernel_desc) {
auto &desc_out_type = kernel_desc.c_type();
std::vector<prb_reqs_t> reqs_vec({kernel_desc.reqs()});
std::vector<int> out_type_sizes({desc_out_type.size()});
extensions_t ext;
for (auto &out_type : get_out_types(kernel_desc)) {
if (out_type.size() == desc_out_type.size()) continue;
auto d = kernel_desc;
auto &tag = get_out_tag(d);
tag = layout_tag_t(tag.desc(), out_type, tag.raw_tag());
if (!create_plan(d, bench_mger.hw())) continue;
if (!try_create(bench_mger, d)) continue;
ext.add(extensions_t::out_size(out_type.size()));
reqs_vec.push_back(d.reqs());
out_type_sizes.push_back(out_type.size());
}
if (kernel_desc.prop == prop_kind::backward_weights
&& !kernel_desc.with_bias_bwd_w()) {
auto d = kernel_desc;
d.bias_type = dsl::type_t::f32();
if (create_plan(d, bench_mger.hw()) && try_create(bench_mger, d)) {
ext.add(extension_kind_t::bias);
reqs_vec.push_back(d.reqs());
out_type_sizes.push_back(desc_out_type.size());
}
}
bool try_stream_k = !kernel_desc.use_stream_k;
try_stream_k &= (kernel_desc.prop != prop_kind::backward_data
|| (kernel_desc.a_type() == dsl::type_t::f32()
&& kernel_desc.b_type() == dsl::type_t::f32()));
try_stream_k &= (!kernel_desc.is_dw
|| kernel_desc.prop == prop_kind::backward_weights);
if (try_stream_k) {
auto d = to_stream_k(kernel_desc, false);
if (!d.is_empty()) {
if (create_plan(d, bench_mger.hw()) && try_create(bench_mger, d)) {
ext.add(extension_kind_t::stream_k);
}
}
}
auto _kernel_desc = kernel_desc;
_kernel_desc.ext = ext;
return _kernel_desc;
}
plan_registry_t::entry_t prepare_plan_registry_entry(
const bench_manager_t &bench_mger, const kernel_desc_t &kernel_desc) {
plan_registry_t::entry_t entry;
auto bd = bench(bench_mger, kernel_desc);
if (!bd) return entry;
model_fit(bd, entry.model_set);
entry.desc = try_extensions(bench_mger, kernel_desc);
if (entry.desc.ext.has(extension_kind_t::stream_k)) {
auto d_sk = to_stream_k(entry.desc);
auto bd = bench(bench_mger, d_sk);
model_fit(bd, entry.model_set);
}
return entry;
}
} } } } } } } }