#include <algorithm>
#include "common/c_types_map.hpp"
#include "common/utils.hpp"
#include "gpu/intel/compute/utils.hpp"
#include "gpu/intel/reorder/generic.hpp"
namespace dnnl {
namespace impl {
namespace gpu {
namespace intel {
namespace reorder {
using namespace dnnl::impl::memory_tracking::names;
struct dimension_t {
dim_t size;
dim_t step;
dim_idx_t idx;
};
using dimensions_t = std::vector<dimension_t>;
dimensions_t dims_by_stride(const memory_desc_wrapper &mdw) {
const auto &desc = mdw.blocking_desc();
const auto &strides = desc.strides;
const auto cmp = [&](const dimension_t &a, const dimension_t &b) {
const auto a_stride = strides[a.idx];
const auto b_stride = strides[b.idx];
return a_stride < b_stride || (a_stride == b_stride && a.size < b.size);
};
const int ndims = mdw.ndims();
dimensions_t dims(ndims);
for (int d = 0; d < ndims; ++d) {
auto &blk = dims[d];
blk.idx = d;
blk.size = mdw.padded_dims()[d];
}
std::sort(dims.begin(), dims.end(), cmp);
return dims;
}
dimensions_t query_dims_and_blocks(const memory_desc_wrapper &mdw) {
auto blocks = dims_by_stride(mdw);
const dim_idx_t ndims = mdw.ndims();
const auto &desc = mdw.blocking_desc();
const dim_idx_t nblks = desc.inner_nblks;
dimensions_t inner_blks(nblks);
std::vector<dim_t> steps(ndims, 1);
dim_t blks_size = 1;
for (int i = nblks - 1; i >= 0; --i) {
auto &blk = inner_blks[i];
blk.idx = into<dim_idx_t>(desc.inner_idxs[i]);
blk.size = desc.inner_blks[i];
blk.step = steps[blk.idx];
steps[blk.idx] *= blk.size;
blks_size *= blk.size;
}
for (auto &blk : blocks) {
blk.step = steps[blk.idx];
blk.size = utils::div_up(blk.size, blk.step);
}
const auto size_1 = [](const dimension_t &b) { return b.size == 1; };
const auto end = blocks.end();
blocks.erase(std::remove_if(blocks.begin(), end, size_1), end);
dim_t stride = blocks.empty() ? 1 : desc.strides[blocks[0].idx];
for (auto &blk : inner_blks) {
if (blk.size == 1) continue; if (blocks.empty() || blocks[0].idx != blk.idx || blks_size != stride) {
blocks.insert(blocks.begin(), blk);
} else {
blk.size *= blocks[0].size;
blocks[0] = blk;
}
blks_size /= blk.size;
stride = blks_size;
}
if (blocks.empty() && ndims > 0) {
dimension_t blk;
blk.idx = 0;
blk.size = 1;
blk.step = 1;
blocks.push_back(blk);
}
return blocks;
}
dimensions_t query_dims_and_blocks(const memory_desc_t &md) {
const memory_desc_wrapper mdw(md);
return query_dims_and_blocks(mdw);
}
bool is_generic_faster_than_ref(
const memory_desc_t &src_md, const memory_desc_t &dst_md) {
const dim_t max_1d_ref_nelems = 512;
const dim_t max_nd_ref_nelems = 512 * 512;
auto nelems
= std::max(utils::array_product(src_md.padded_dims, src_md.ndims),
utils::array_product(dst_md.padded_dims, dst_md.ndims));
if (src_md.ndims == 1 && dst_md.ndims == 1)
return nelems > max_1d_ref_nelems;
auto src_blks = query_dims_and_blocks(src_md);
auto dst_blks = query_dims_and_blocks(dst_md);
if (src_blks.empty() || dst_blks.empty()) return false;
auto src_inner_idx = src_blks[0].idx;
auto dst_inner_idx = dst_blks[0].idx;
auto scale = (src_inner_idx != dst_inner_idx) ? 2 : 1;
return nelems > scale * max_nd_ref_nelems;
}
using dim_pair_t = std::array<dimension_t, 2>;
bool equal_blocks(const dim_pair_t &a, const dim_pair_t &b) {
return (a[0].size == b[0].size && a[1].size == b[1].size);
}
void combine(memory_desc_t &md, int i, int j) {
const int new_ndims = md.ndims - 1;
if (new_ndims == 0) return; auto &desc = md.format_desc.blocking;
auto &strides = desc.strides;
const int outer = strides[i] < strides[j] ? j : i;
const int inner = strides[i] < strides[j] ? i : j;
const auto outer_stride = strides[outer];
const auto outer_size = md.padded_dims[outer];
md.offset0 += strides[outer] * md.padded_offsets[outer];
md.dims[i] = md.dims[outer] * md.padded_dims[inner];
md.padded_dims[i] = md.padded_dims[i] * md.padded_dims[j];
md.padded_offsets[i] = md.padded_offsets[inner];
strides[i] = strides[inner];
for (int k = j; k < new_ndims; ++k) {
md.dims[k] = md.dims[k + 1];
md.padded_dims[k] = md.padded_dims[k + 1];
md.padded_offsets[k] = md.padded_offsets[k + 1];
strides[k] = strides[k + 1];
}
md.dims[new_ndims] = 0;
md.padded_dims[new_ndims] = 0;
md.padded_offsets[new_ndims] = 0;
strides[new_ndims] = 0;
auto &idxs = desc.inner_idxs;
auto &blks = desc.inner_blks;
int nblks = desc.inner_nblks;
auto blks_size = utils::array_product(blks, nblks);
int count = 0;
bool last_is_combined = false;
dim_t blocks = 1;
for (int k = 0; k < nblks; ++k) {
if (idxs[k] == i || idxs[k] == j) {
blocks *= blks[k];
if (count == 0 && strides[i] == blks_size) {
md.dims[i] = md.padded_dims[i];
strides[i] /= blks[k];
blks_size /= blks[k];
} else if (last_is_combined) {
blks[count - 1] *= blks[k];
} else {
last_is_combined = true;
blks[count] = blks[k];
idxs[count] = i;
count++;
}
continue;
}
last_is_combined = false;
blks[count] = blks[k];
idxs[count] = (idxs[k] > j ? idxs[k] - 1 : idxs[k]);
count++;
}
auto outer_step = utils::div_up(outer_size, blocks);
for (int k = 0; k < new_ndims; ++k) {
if (strides[k] == outer_stride) strides[k] *= outer_step;
}
desc.inner_nblks = count;
md.ndims = new_ndims;
}
void remove_bit(int &mask, int bit) {
const int lower_bits = (1 << bit) - 1;
mask = (mask & lower_bits) | ((mask >> 1) & ~lower_bits);
}
int extended_dims(const memory_desc_t &md) {
int mask = 0;
const int ndims = md.ndims;
const auto &blkg = md.format_desc.blocking;
const int nblks = blkg.inner_nblks;
auto dims = dims_by_stride(md);
std::vector<dim_t> blocks(ndims, 1);
dim_t expected_stride = 1;
for (int i = 0; i < nblks; ++i) {
auto idx = blkg.inner_idxs[i];
auto blks = blkg.inner_blks[i];
blocks[idx] *= blks;
expected_stride *= blks;
}
for (int i = 0; i < ndims; ++i) {
const auto &dim = dims[i];
auto stride = blkg.strides[dim.idx];
auto step = utils::div_up(dim.size, blocks[dim.idx]);
if (stride != expected_stride) {
mask |= (1 << dim.idx);
expected_stride = stride;
}
expected_stride *= step;
}
return mask;
}
struct pair_filter_t {
public:
using value_type = dim_pair_t;
private:
using const_dim_iterator_t = typename dimensions_t::const_iterator;
using predicate_t = std::function<bool(const value_type &)>;
public:
struct iterator_t {
bool operator==(const iterator_t &o) const { return it == o.it; }
bool operator!=(const iterator_t &o) const { return it != o.it; }
value_type operator*() const { return {*it, *(it + 1)}; }
iterator_t &operator++() {
advance();
return *this;
}
iterator_t operator++(int) {
auto cpy = *this;
advance();
return cpy;
}
iterator_t(const_dim_iterator_t it, const_dim_iterator_t end,
predicate_t pred)
: it(it), end(end), pred(std::move(pred)) {
advance(true);
}
private:
void advance(bool check_first = false) {
if (it == end || (check_first && pred(operator*()))) return;
while (++it != end && !pred(operator*())) {}
}
const_dim_iterator_t it, end;
predicate_t pred;
};
iterator_t begin() const { return {begin_, end_ - 1, pred}; }
iterator_t end() const { return {end_ - 1, end_ - 1, pred}; }
bool empty() const { return begin() == end(); }
pair_filter_t(const dimensions_t &iter, const predicate_t &pred)
: begin_(iter.begin()), end_(iter.end()), pred(pred) {}
private:
const_dim_iterator_t begin_, end_;
predicate_t pred;
};
#define NO_IDX dim_idx::invalid
dim_idx_t successor(const dimensions_t &a, dim_idx_t idx) {
dim_idx_t succ;
auto match_idx = [&](const dim_pair_t &p) { return p[0].idx == idx; };
auto match_xor = [&](const dim_pair_t &p) {
return match_idx(p) ^ (p[1].idx == succ);
};
if (a.back().idx == idx) return NO_IDX;
auto filtered = pair_filter_t(a, match_idx);
if (filtered.empty()) return idx;
succ = (*filtered.begin())[1].idx;
if (a.front().idx == succ) return NO_IDX;
if (!pair_filter_t(a, match_xor).empty()) return NO_IDX;
return succ;
}
dim_idx_t successor(
const dimensions_t &a, const dimensions_t &b, dim_idx_t idx) {
auto succ = successor(a, idx);
if (succ == NO_IDX || succ != successor(b, idx)) return NO_IDX;
auto pred = [&](const dim_pair_t &p) { return p[0].idx == idx; };
pair_filter_t iter_a(a, pred);
pair_filter_t iter_b(b, pred);
auto it_a = iter_a.begin();
auto it_b = iter_b.begin();
const auto end_a = iter_a.end();
const auto end_b = iter_b.end();
for (; it_a != end_a && it_b != end_b; ++it_a, ++it_b) {
if (!equal_blocks(*it_a, *it_b)) return NO_IDX;
}
return (it_a != end_a || it_b != end_b) ? NO_IDX : succ;
}
bool can_be_combined(dim_idx_t idx, int mask) {
return !(idx == NO_IDX || (mask & (1 << idx)));
}
void compress(memory_desc_t &a, memory_desc_t &b, int &a_mask, int &b_mask) {
const auto blks_a = query_dims_and_blocks(a);
const auto blks_b = query_dims_and_blocks(b);
const int skip_mask = a_mask | b_mask | extended_dims(a) | extended_dims(b);
const dim_idx_t ndims = a.ndims;
std::vector<dim_idx_t> successors(ndims, NO_IDX);
std::vector<dim_idx_t> aliases(ndims);
for (dim_idx_t i = 0; i < ndims; ++i) {
aliases[i] = i;
if ((a_mask | b_mask) & (1 << i)) continue;
auto succ = successor(blks_a, blks_b, i);
if (!can_be_combined(succ, skip_mask)) continue;
successors[i] = succ;
}
for (int i = ndims - 1; i >= 0; --i) {
dim_idx_t succ = successors[i];
if (succ == NO_IDX) continue;
while (succ != aliases[succ])
succ = aliases[succ];
dim_idx_t from = std::max<dim_idx_t>(i, succ);
dim_idx_t into = std::min<dim_idx_t>(i, succ);
combine(a, into, from);
combine(b, into, from);
remove_bit(a_mask, from);
remove_bit(b_mask, from);
aliases[from] = into;
}
}
#undef NO_IDX
void fix_steps(dimensions_t &blk, const dimensions_t &pkt) {
dim_t steps[MAX_NDIMS] = {1, 1, 1, 1, 1, 1};
for (size_t i = 0; i < pkt.size(); i++) {
steps[pkt[i].idx] *= pkt[i].size;
}
for (size_t i = 0; i < blk.size(); i++) {
blk[i].step = steps[blk[i].idx];
steps[blk[i].idx] *= blk[i].size;
}
}
dimensions_t find_missing_blocks(
const dimensions_t &all, dimensions_t subset, bool round_up) {
dimensions_t ret;
for (size_t ia = 0; ia < all.size(); ia++) {
dimension_t from_a = all[ia];
for (size_t ib = 0; ib < subset.size(); ib++) {
if (subset[ib].idx == from_a.idx) {
auto smaller = std::min(from_a.size, subset[ib].size);
if (round_up) {
from_a.size = utils::div_up(from_a.size, smaller);
subset[ib].size = utils::div_up(subset[ib].size, smaller);
} else {
from_a.size /= smaller;
subset[ib].size /= smaller;
}
}
}
if (from_a.size > 1) { ret.push_back(from_a); }
}
return ret;
}
dimensions_t remainder(const dimensions_t &all, const dimensions_t &subset) {
dimensions_t ret;
for (size_t i = 0; i < all.size(); i++) {
if (i < subset.size()) {
if (all[i].size == subset[i].size) {
continue;
} else {
dimension_t item;
item.idx = all[i].idx;
item.size = all[i].size / subset[i].size;
item.step = all[i].step * subset[i].size;
ret.push_back(item);
}
} else {
ret.push_back(all[i]);
}
}
return ret;
}
bool fill_to_vect(
int simd_size, const dimensions_t &all, dimensions_t &subset) {
const int min_full_vecs = 5; dim_t current_size = 1;
subset.clear();
for (auto &dim : all) {
dim_t next_size = current_size * dim.size;
dim_t next_full_vecs = next_size / simd_size;
if (next_full_vecs >= min_full_vecs || next_size % simd_size == 0) {
dimension_t tmp = dim;
tmp.size = simd_size / current_size;
subset.push_back(tmp);
return true;
}
if (simd_size % next_size != 0) return false;
current_size = next_size;
subset.push_back(dim);
}
return false;
}
bool add_to_vector(dimensions_t &v, const dimension_t &item) {
if (v.empty() || item.idx != v.back().idx) {
if (v.size() >= LOOP_NEST_LEVEL) { return false; }
v.push_back(item);
v.back().size = item.size;
} else {
v.back().size *= item.size;
}
return true;
}
bool no_more_such_idx(dimensions_t &vect, size_t iter) {
const dim_idx_t idx_to_search_for = vect[iter].idx;
for (size_t i = iter + 1; i < vect.size(); i++) {
if (vect[i].idx == idx_to_search_for) { return false; }
}
return true;
}
dimensions_t fix_order_to(dimensions_t input, dimensions_t ref) {
dimensions_t ret;
for (size_t i = 0; i < ref.size(); i++) {
for (size_t j = 0; j < input.size(); j++) {
if (ref[i].size != 1 && input[j].size != 1
&& ref[i].idx == input[j].idx) {
dim_t smaller = std::min(ref[i].size, input[j].size);
if (no_more_such_idx(ref, i) || j == input.size() - 1) {
smaller = input[j].size;
}
dimension_t item = ref[i];
item.size = smaller;
ref[i].size = utils::div_up(ref[i].size, smaller);
input[j].size = utils::div_up(input[j].size, smaller);
add_to_vector(ret, item);
}
}
}
for (size_t i = 0; i < input.size(); i++) {
if (input[i].size > 1) { add_to_vector(ret, input[i]); }
}
return ret;
}
dim_t check_size(const dimensions_t &block) {
dim_t length = 1;
for (size_t i = 0; i < block.size(); i++) {
length *= block[i].size;
}
return length;
}
size_t check_burst_length(dimensions_t all, dimensions_t subset) {
size_t length = 1;
for (size_t i = 0; i < all.size(); i++) {
for (size_t j = 0; j < subset.size(); j++) {
if (all[i].idx == subset[j].idx) {
auto smaller = std::min(all[i].size, subset[j].size);
length *= (int)smaller;
all[i].size /= smaller;
subset[j].size /= smaller;
}
}
if (all[i].size != 1) {
return length;
} }
return length;
}
bool increase_burst(dimensions_t all, dimensions_t &subset, dimensions_t &other,
size_t itemlimit, size_t current_size, size_t optimal_size) {
const dim_t space_coeff = itemlimit / check_size(subset);
const dim_t request_coeff = utils::div_up(optimal_size, current_size);
dimensions_t subset_copy = subset;
if (space_coeff < 2) { return false; }
for (size_t i = 0; i < all.size(); i++) {
for (size_t j = 0; j < subset_copy.size(); j++) {
if (all[i].idx == subset_copy[j].idx) {
auto smaller = std::min(all[i].size, subset_copy[j].size);
all[i].size /= smaller;
subset_copy[j].size /= smaller;
}
}
if (all[i].size != 1) {
auto incr = std::min(space_coeff, all[i].size);
incr = std::min(incr, request_coeff);
all[i].size = incr;
bool success = add_to_vector(subset, all[i]);
if (!success) { return false; }
add_to_vector(other, all[i]);
return true;
}
}
return false;
}
bool split_into_blocks_and_packets(size_t vect, size_t optimal_burst_bytes,
size_t memlimit_bytes, size_t sizeof_src, size_t sizeof_dst,
const dimensions_t &src, const dimensions_t &dst,
dimensions_t &src_packet, dimensions_t &src_block,
dimensions_t &dst_packet, dimensions_t &dst_block) {
if (!fill_to_vect((int)vect, src, src_packet)) { return false; }
if (!fill_to_vect((int)vect, dst, dst_packet)) { return false; }
dimensions_t sremainder = remainder(src, src_packet);
dimensions_t dremainder = remainder(dst, dst_packet);
src_block = find_missing_blocks(dst_packet, src_packet, true);
dst_block = find_missing_blocks(src_packet, dst_packet, false);
size_t burst_size_src
= vect * sizeof_src * check_burst_length(sremainder, src_block);
size_t burst_size_dst
= vect * sizeof_dst * check_burst_length(dremainder, dst_block);
bool success = true;
size_t itemlimit = memlimit_bytes / (vect * sizeof_src);
while (success
&& (burst_size_src < optimal_burst_bytes
|| burst_size_dst < optimal_burst_bytes)) {
if (burst_size_src < burst_size_dst) {
success = increase_burst(sremainder, src_block, dst_block,
itemlimit, burst_size_src, optimal_burst_bytes);
} else {
success = increase_burst(dremainder, dst_block, src_block,
itemlimit, burst_size_dst, optimal_burst_bytes);
}
burst_size_src
= vect * sizeof_src * check_burst_length(sremainder, src_block);
burst_size_dst
= vect * sizeof_dst * check_burst_length(dremainder, dst_block);
}
src_block = fix_order_to(src_block, std::move(sremainder));
dst_block = fix_order_to(dst_block, std::move(dremainder));
fix_steps(src_block, src_packet);
fix_steps(dst_block, dst_packet);
return true;
}
bool fill_conf_vld(const memory_desc_wrapper &src,
const memory_desc_wrapper &dst, int scale_mask, size_t memlimit_bytes,
size_t optimal_burst_bytes, vectorize_last_dim_t &cfg,
dim_idx_t &vect_dim, int &vect_size, dim_t *blocks) {
const dimensions_t src_dims = query_dims_and_blocks(src);
const dimensions_t dst_dims = query_dims_and_blocks(dst);
dimensions_t src_packet, src_block, dst_packet, dst_block;
bool success = split_into_blocks_and_packets(16, memlimit_bytes,
optimal_burst_bytes, src.data_type_size(), dst.data_type_size(),
src_dims, dst_dims, src_packet, src_block, dst_packet, dst_block);
if (!success) { return false; }
cfg.src_vect_limit = (int)check_burst_length(src_packet, src_packet);
cfg.dst_vect_limit = (int)check_burst_length(dst_packet, dst_packet);
for (size_t i = 0; i < LOOP_NEST_LEVEL; i++) {
cfg.src_vct[i].blk_size = 1;
cfg.dst_vct[i].blk_size = 1;
cfg.src_blk[i].blk_size = 1;
cfg.dst_blk[i].blk_size = 1;
cfg.src_vct[i].step_size = 1;
cfg.dst_vct[i].step_size = 1;
cfg.src_blk[i].step_size = 1;
cfg.dst_blk[i].step_size = 1;
cfg.src_vct[i].dim_idx = 0;
cfg.dst_vct[i].dim_idx = 0;
cfg.src_blk[i].dim_idx = 0;
cfg.dst_blk[i].dim_idx = 0;
}
cfg.src_vct[0].blk_size = into<int>(src_packet[0].size);
cfg.src_vct[0].dim_idx = src_packet[0].idx;
cfg.dst_vct[0].blk_size = into<int>(dst_packet[0].size);
cfg.dst_vct[0].dim_idx = dst_packet[0].idx;
for (size_t i = 0; i < src_packet.size(); i++) {
cfg.src_vct[i].dim_idx = src_packet[i].idx;
cfg.src_vct[i].blk_size = into<int>(src_packet[i].size);
cfg.src_vct[i].step_size = into<int>(src_packet[i].step);
}
for (size_t i = 0; i < dst_packet.size(); i++) {
cfg.dst_vct[i].dim_idx = dst_packet[i].idx;
cfg.dst_vct[i].blk_size = into<int>(dst_packet[i].size);
cfg.dst_vct[i].step_size = into<int>(dst_packet[i].step);
}
for (size_t i = 0; i < src_block.size(); i++) {
cfg.src_blk[i].dim_idx = src_block[i].idx;
cfg.src_blk[i].blk_size = into<int>(src_block[i].size);
cfg.src_blk[i].step_size = into<int>(src_block[i].step);
}
for (size_t i = 0; i < dst_block.size(); i++) {
cfg.dst_blk[i].dim_idx = dst_block[i].idx;
cfg.dst_blk[i].blk_size = into<int>(dst_block[i].size);
cfg.dst_blk[i].step_size = into<int>(dst_block[i].step);
}
cfg.vector_dim = dst_packet[0].idx;
vect_dim = dst_packet[0].idx;
vect_size = 16;
for (int i = 0; i < LOOP_NEST_LEVEL; i++) {
if (cfg.dst_blk[i].blk_size != 1) {
blocks[cfg.dst_blk[i].dim_idx] *= cfg.dst_blk[i].blk_size;
}
}
cfg.rescale_coeff = 16;
for (int i = 0; i < LOOP_NEST_LEVEL; i++) {
auto db = cfg.dst_vct[i];
blocks[db.dim_idx] *= db.blk_size;
}
return true;
}
status_t generic_t::pd_t::init_conf(impl::engine_t *engine) {
using namespace format_tag;
size_t memlimit_bytes;
size_t optimal_burst_bytes;
const memory_desc_wrapper original_src_mdw(src_md());
const memory_desc_wrapper original_dst_mdw(dst_md());
quantization_t src_quant(attr(), original_src_mdw, DNNL_ARG_SRC);
quantization_t dst_quant(attr(), original_dst_mdw, DNNL_ARG_DST);
auto src_mask = src_quant.scale_mask();
auto dst_mask = dst_quant.scale_mask();
memory_desc_t new_a;
memory_desc_t new_b;
primitive_attr_t attr_copy = *attr();
memcpy(&new_a, src_md(), sizeof(new_a));
memcpy(&new_b, dst_md(), sizeof(new_b));
compress(new_a, new_b, src_mask, dst_mask);
if (src_mask >= 0) { CHECK(attr_copy.scales_.set(DNNL_ARG_SRC, src_mask)); }
if (dst_mask >= 0) { CHECK(attr_copy.scales_.set(DNNL_ARG_DST, dst_mask)); }
VDISPATCH_REORDER_IC(is_generic_faster_than_ref(new_a, new_b),
VERBOSE_SKIP_PRIMITIVE_IMPL);
const memory_desc_wrapper src_mdw(new_a);
const memory_desc_wrapper dst_mdw(new_b);
conf.src_md_info = memory_desc_info_t::create(src_mdw);
conf.dst_md_info = memory_desc_info_t::create(dst_mdw);
conf.require_stateless_addressing = has_large_buffers();
conf.src_quant = {&attr_copy, src_mdw, DNNL_ARG_SRC};
conf.dst_quant = {&attr_copy, dst_mdw, DNNL_ARG_DST};
conf.sum_quant = {&attr_copy};
status_t status = status::success;
const auto &padded_dims = dst_mdw.padded_dims();
conf.has_padding = !src_mdw.is_dense() || !dst_mdw.is_dense();
conf.ndims = src_mdw.ndims();
conf.nelems = utils::array_product(padded_dims, conf.ndims);
conf.sub_group_size = 1;
if (conf.nelems == 0) { return status::success; }
auto *intel_engine = utils::downcast<intel::engine_t *>(engine);
memlimit_bytes = 2048;
optimal_burst_bytes = 64;
dim_t blocks[MAX_NDIMS] = {1, 1, 1, 1, 1, 1};
int vect_size = 1;
dim_idx_t vect_dim = 0;
VDISPATCH_REORDER_IC(
fill_conf_vld(src_mdw, dst_mdw, src_mask | dst_mask, memlimit_bytes,
optimal_burst_bytes, conf.aux_data.vld, vect_dim, vect_size,
&blocks[0]),
VERBOSE_BAD_PARAM, "conf_vld");
conf.sub_group_size = vect_size;
conf.dispatch = intel_engine->create_dispatch(dst_mdw.md_);
for (dim_idx_t i = 0; i < MAX_NDIMS; ++i) {
auto dim_str = utils::format("D%d", i);
if (i < into<dim_idx_t>(dst_mdw.ndims())) {
uint64_t dim = padded_dims[i];
if (i == vect_dim) {
dim = utils::rnd_up(dim, blocks[i]);
dim *= 16;
}
conf.dispatch.define_dim(dim_str, i, dim, blocks[i]);
} else {
conf.dispatch.define_dim(dim_str, 1);
}
}
if (vect_size != 1) {
const auto dim_str = utils::format("D%d", vect_dim);
CHECK(conf.dispatch.vectorize_dim(dim_str, vect_size));
}
conf.dispatch.generate();
return status;
}
status_t generic_t::pd_t::init_kernel_ctx(
compute::kernel_ctx_t &kernel_ctx) const {
using namespace format_tag;
const memory_desc_wrapper src_mdw(src_md());
const memory_desc_wrapper dst_mdw(dst_md());
if (conf.nelems == 0) return status::success;
kernel_ctx.define_int("NDIMS", conf.ndims);
kernel_ctx.add_option("-cl-std=CL2.0");
conf.src_quant.define_macros(kernel_ctx, "SRC");
conf.dst_quant.define_macros(kernel_ctx, "DST");
conf.sum_quant.define_macros(kernel_ctx, "SUM");
def_dispatch(kernel_ctx, conf.dispatch);
kernel_ctx.require_stateless_addressing(conf.require_stateless_addressing);
kernel_ctx.define_int("SUB_GROUP_SIZE", conf.sub_group_size);
kernel_ctx.define_int("PAD_FILL_ZERO", conf.has_padding);
def_memory_desc_info(kernel_ctx, conf.src_md_info, "SRC");
def_memory_desc_info(kernel_ctx, conf.dst_md_info, "DST");
kernel_ctx.define_int("GENERIC_REORDER", 1);
kernel_ctx.define_int("VECT_DIM", conf.aux_data.vld.vector_dim);
kernel_ctx.define_int("VECT_SIZE", conf.sub_group_size);
kernel_ctx.define_int("RESCALE_COEFF", conf.aux_data.vld.rescale_coeff);
kernel_ctx.define_int("LIMIT_SSGID", conf.aux_data.vld.src_vect_limit);
kernel_ctx.define_int("LIMIT_DSGID", conf.aux_data.vld.dst_vect_limit);
compute::nd_range_t nd_range = conf.dispatch.nd_range();
const auto &lws = nd_range.local_range();
if (!lws) return status::runtime_error;
kernel_ctx.define_int("SG_PER_WG", lws.nelems() / conf.sub_group_size);
int i = 0;
int cache_dim[MAX_NDIMS] = {1, 1, 1, 1, 1, 1};
while (i < LOOP_NEST_LEVEL) {
cache_dim[conf.aux_data.vld.dst_vct[i].dim_idx]
*= conf.aux_data.vld.dst_vct[i].blk_size;
cache_dim[conf.aux_data.vld.dst_blk[i].dim_idx]
*= conf.aux_data.vld.dst_blk[i].blk_size;
kernel_ctx.define_int(std::string("S_BLK_SIZE_") + std::to_string(i),
conf.aux_data.vld.src_blk[i].blk_size);
kernel_ctx.define_int(std::string("S_BLK_STEP_") + std::to_string(i),
conf.aux_data.vld.src_blk[i].step_size);
kernel_ctx.define_int(std::string("S_BLK_IDX_") + std::to_string(i),
conf.aux_data.vld.src_blk[i].dim_idx);
kernel_ctx.define_int(std::string("D_BLK_SIZE_") + std::to_string(i),
conf.aux_data.vld.dst_blk[i].blk_size);
kernel_ctx.define_int(std::string("D_BLK_STEP_") + std::to_string(i),
conf.aux_data.vld.dst_blk[i].step_size);
kernel_ctx.define_int(std::string("D_BLK_IDX_") + std::to_string(i),
conf.aux_data.vld.dst_blk[i].dim_idx);
i++;
}
int cache_stride = 1;
for (int i = 0; i < MAX_NDIMS; i++) {
kernel_ctx.define_int(
std::string("CACHE_STRIDE_") + std::to_string(i), cache_stride);
cache_stride *= cache_dim[i];
}
int s_size_so_far = 1;
int d_size_so_far = 1;
for (int i = 0; i < LOOP_NEST_LEVEL; i++) {
auto s = conf.aux_data.vld.src_vct[i];
auto d = conf.aux_data.vld.dst_vct[i];
kernel_ctx.define_int(
std::string("S_MOD_") + std::to_string(i), s.blk_size);
kernel_ctx.define_int(
std::string("S_DIV_") + std::to_string(i), s_size_so_far);
kernel_ctx.define_int(
std::string("S_MUL_") + std::to_string(i), s.step_size);
kernel_ctx.define_int(
std::string("S_IDX_") + std::to_string(i), s.dim_idx);
kernel_ctx.define_int(
std::string("D_MOD_") + std::to_string(i), d.blk_size);
kernel_ctx.define_int(
std::string("D_DIV_") + std::to_string(i), d_size_so_far);
kernel_ctx.define_int(
std::string("D_MUL_") + std::to_string(i), d.step_size);
kernel_ctx.define_int(
std::string("D_IDX_") + std::to_string(i), d.dim_idx);
s_size_so_far *= s.blk_size;
d_size_so_far *= d.blk_size;
}
return status::success;
}
void generic_t::pd_t::init_scratchpad() {
if (conf.src_quant.with_scale()) {
auto scratchpad = scratchpad_registry().registrar();
scratchpad.book(memory_tracking::names::key_reorder_src_scales,
conf.src_quant.num_scales(), sizeof(float),
OCL_BUFFER_ALIGNMENT);
}
if (conf.dst_quant.with_scale()) {
auto scratchpad = scratchpad_registry().registrar();
scratchpad.book(memory_tracking::names::key_reorder_dst_scales,
conf.dst_quant.num_scales(), sizeof(float),
OCL_BUFFER_ALIGNMENT);
}
}
status_t generic_t::execute(const exec_ctx_t &ctx) const {
status_t status = status::success;
auto &src = CTX_IN_STORAGE(DNNL_ARG_FROM);
auto &dst = CTX_OUT_STORAGE(DNNL_ARG_TO);
CHECK(status);
const auto &conf = pd()->conf;
if (conf.nelems == 0) { return status::success; }
compute::kernel_arg_list_t arg_list;
arg_list.set(0, src);
arg_list.set(1, dst);
arg_list.set(2, conf.src_quant.scales(ctx));
arg_list.set(3, conf.src_quant.zero_points(ctx));
arg_list.set(4, conf.dst_quant.scales(ctx));
arg_list.set(5, conf.dst_quant.zero_points(ctx));
arg_list.set(6, conf.sum_quant.scales());
arg_list.set(7, conf.sum_quant.zero_points());
auto nd_range = conf.dispatch.nd_range();
return large_parallel_for(ctx, nd_range, kernel_, arg_list, 8);
}
} } } } }