#ifdef __MMA__
#include <cstdint>
#if defined(_MSC_VER)
#include <malloc.h>
#endif
#include <iostream>
#include <vector>
#include "common/dnnl_traits.hpp"
#include "common/nstl.hpp"
#include "common/utils.hpp"
#include "oneapi/dnnl/dnnl_types.h"
#include "cpu/platform.hpp"
#include "cpu/gemm/f32/gemm_utils_f32.hpp"
#include "cpu/gemm/gemm.hpp"
#include "cpu/ppc64/gemm/gemm_driver.hpp"
#include "cpu/ppc64/gemm/gemm_info.hpp"
#include "cpu/ppc64/gemm/gemm_utils.hpp"
#include "cpu/ppc64/ppc64_gemm_s8x8s32.hpp"
namespace dnnl {
namespace impl {
namespace cpu {
namespace ppc64 {
#define MAX_STACK_SZ 8192
template <typename c_type>
struct alignas(64) gemm_per_thread_t {
volatile int32_t result;
volatile int32_t compute_done;
int32_t thr_k_stride;
int32_t nthr_k;
dim_t ldc_local;
dim_t ldc_global;
c_type *c_local;
c_type *volatile c_global;
gemm_slice_t slice;
};
template <typename T>
int get_vector_length() {
int v_bytes = 16;
return v_bytes / sizeof(T);
}
template <typename c_type>
static inline void round_to_nearest(c_type *rounded_val, double fp_val) {
if (fp_val >= 0.) {
fp_val += 0.5;
if (fp_val > INT32_MAX) { fp_val = INT32_MAX; }
} else {
fp_val -= 0.5;
if (fp_val < INT32_MIN) { fp_val = INT32_MIN; }
}
*rounded_val = (c_type)fp_val;
}
template <typename mat_t>
static void sum_matrices(dim_t m, dim_t n, mat_t *__restrict dst, dim_t ld_dst,
mat_t *__restrict src, dim_t ld_src) {
for (dim_t j = 0; j < n; j++) {
PRAGMA_OMP_SIMD()
for (int i = 0; i < m; i++)
dst[i + j * ld_dst] += src[i + j * ld_src];
}
}
template <typename c_type>
static void sum_k_blocks(
int ithr, gemm_per_thread_t<c_type> *thread_arg, bool wait) {
auto m = thread_arg[ithr].slice.m;
auto n = thread_arg[ithr].slice.n;
auto ithr_k = thread_arg[ithr].slice.ithr_k;
auto nthr_k = thread_arg[ithr].nthr_k;
auto stride = thread_arg[ithr].thr_k_stride;
dim_t n0, nn;
partition_1d(ithr_k, nthr_k, n, n0, nn);
auto get_thread_arg = [&](int thr_k) -> gemm_per_thread_t<c_type> & {
return thread_arg[ithr + (thr_k - ithr_k) * stride];
};
auto wait_thread = [&](int thr_k) {
if (wait) {
auto &tk_arg = get_thread_arg(thr_k);
while (!tk_arg.compute_done) {}
}
};
auto add_thread_results = [&](int thr_k) {
auto &tk_arg = get_thread_arg(thr_k);
sum_matrices(m, nn, tk_arg.c_global + n0 * tk_arg.ldc_global,
tk_arg.ldc_global, tk_arg.c_local + n0 * tk_arg.ldc_local,
tk_arg.ldc_local);
};
if (ithr_k > 0) {
wait_thread(0);
add_thread_results(ithr_k);
}
for (int thr_k = 1; thr_k < nthr_k; thr_k++) {
if (thr_k != ithr_k) {
wait_thread(thr_k);
add_thread_results(thr_k);
}
}
}
template <typename c_type>
static inline void add_results(const dim_t m, const dim_t n, const float alpha,
const float beta, const c_type *c_partial_sum, const dim_t ldcp,
c_type *c_data, const dim_t ldc, const c_type *co,
offset_type offsetc) {
constexpr bool is_int8 = data_traits_t<c_type>::data_type == data_type::s32;
for (dim_t j = 0; j < n; ++j) {
for (dim_t i = 0; i < m; ++i) {
c_type ctemp = c_partial_sum[i + j * ldcp];
if (alpha == 1.0f) {
if (beta == 0.0f) {
c_data[i + j * ldc] = ctemp;
} else {
if (is_int8) {
double c_float
= (double)beta * (double)c_data[i + j * ldc];
c_float += (double)ctemp;
round_to_nearest(&c_data[i + j * ldc], c_float);
} else {
c_data[i + j * ldc] *= beta;
c_data[i + j * ldc] += ctemp;
}
}
} else if (alpha == -1.0f) {
if (beta == 0.0f) {
c_data[i + j * ldc] = -ctemp;
} else {
if (is_int8) {
double c_float
= (double)beta * (double)c_data[i + j * ldc];
c_float -= (double)ctemp;
round_to_nearest(&c_data[i + j * ldc], c_float);
} else {
c_data[i + j * ldc] *= beta;
c_data[i + j * ldc] -= ctemp;
}
}
} else {
if (beta == 0.0f) {
if (is_int8) {
double c_float = alpha * (double)ctemp;
round_to_nearest(&c_data[i + j * ldc], c_float);
} else {
c_data[i + j * ldc] = alpha * ctemp;
}
} else {
if (is_int8) {
double c_float = alpha * (double)ctemp
+ beta * (double)c_data[i + j * ldc];
round_to_nearest(&c_data[i + j * ldc], c_float);
} else {
c_data[i + j * ldc] *= beta;
c_data[i + j * ldc] += alpha * ctemp;
}
}
}
if (offsetc == offset_type::fixed) {
c_data[i + j * ldc] += co[0];
} else if (offsetc == offset_type::row) {
c_data[i + j * ldc] += co[j];
} else if (offsetc == offset_type::column) {
c_data[i + j * ldc] += co[i];
}
}
}
}
template <typename a_type, typename b_type, typename c_type>
static inline dim_t get_k_padd(
int ithr, dim_t k, const gemm_info_t<a_type, b_type, c_type> *arg) {
if (arg->a_packed) {
dim_t block_m, block_k;
arg->a_packed->get_blocking(ithr, block_m, block_k);
return block_k;
} else if (arg->b_packed) {
dim_t block_n, block_k;
arg->b_packed->get_blocking(ithr, block_k, block_n);
return block_k;
} else {
dim_t k_padd = 0;
if (k <= arg->bk_traditional) {
k_padd = utils::rnd_up(k, arg->uk);
k_padd = nstl::max(dim_t(128), k_padd);
} else if (k < 2 * arg->bk)
k_padd = utils::rnd_up((k + 1) / 2, arg->uk);
else
k_padd = arg->bk;
return k_padd;
}
}
template <typename a_type, typename b_type, typename c_type>
static inline dim_t get_m_padd(
int ithr, dim_t m, const gemm_info_t<a_type, b_type, c_type> *arg) {
if (arg->a_packed) {
dim_t block_m, block_k;
arg->a_packed->get_blocking(ithr, block_m, block_k);
return block_m;
} else
return utils::rnd_up(
nstl::min(nstl::max(m, arg->um), arg->bm), arg->um);
}
template <typename a_type, typename b_type, typename c_type>
static inline dim_t get_m_padd_parallel_a(int ithr, dim_t m,
const gemm_info_t<a_type, b_type, c_type> *arg, int nthrs) {
auto m_padd = get_m_padd(ithr, m, arg);
if (!arg->a_packed) {
constexpr auto multiplier = 10;
m_padd *= nstl::min(nthrs, multiplier);
if (m_padd > m) m_padd = utils::rnd_up(m, arg->um);
}
return m_padd;
}
template <typename a_type, typename b_type, typename c_type>
static inline dim_t get_n_padd(int ithr, dim_t n, dim_t k,
const gemm_info_t<a_type, b_type, c_type> *arg) {
if (arg->b_packed) {
dim_t block_n, block_k;
arg->b_packed->get_blocking(ithr, block_k, block_n);
return block_n;
} else {
auto bn = (k < arg->blocking_small_k) ? arg->bn_small_k : arg->bn;
return utils::rnd_up(nstl::min(nstl::max(n, arg->un), bn), arg->un);
}
}
static inline void *align(void *ptr, size_t alignment) {
return (void *)utils::rnd_up((uintptr_t)ptr, alignment);
}
template <typename a_type, typename b_type, typename c_type>
void gemm_kernel(dim_t m, dim_t n, const dim_t k, const float alpha,
const a_type *a, const uint8_t *b, float beta, c_type *c,
const dim_t ldc, const c_type *a_row_sum, const c_type *b_col_sum,
c_type *row_offset_ws, c_type *col_offset_ws, const c_type *co,
offset_type offsetc, const gemm_info_t<a_type, b_type, c_type> *arg) {
bool col_req = false;
bool row_req = false;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
dim_t m_stk = col_offset_ws ? 1 : m;
dim_t n_stk = row_offset_ws ? 1 : n;
#if 1
std::vector<c_type> col_offset_stk_vec(m_stk);
std::vector<c_type> row_offset_stk_vec(n_stk);
c_type *col_offset_stk = col_offset_stk_vec.data();
c_type *row_offset_stk = row_offset_stk_vec.data();
#else#endif
c_type *col_offset = col_offset_ws ? col_offset_ws : col_offset_stk;
c_type *row_offset = row_offset_ws ? row_offset_ws : row_offset_stk;
if (is_int8) {
c_type ao = arg->ao;
c_type bo = arg->bo;
c_type co_0 = offsetc == offset_type::none ? 0 : co[0];
if (bo != 0 || offsetc == offset_type::column) col_req = true;
if (ao != 0 || offsetc == offset_type::row) row_req = true;
if ((ao != 0 && bo != 0)
|| (offsetc == offset_type::fixed && co_0 != 0)) {
if (!col_req && !row_req) {
if (m <= n) {
col_req = true;
} else {
row_req = true;
}
}
}
if (col_req) {
for (dim_t i = 0; i < m; i++)
col_offset[i] = 0;
if (offsetc == offset_type::column) {
for (dim_t i = 0; i < m; i++)
col_offset[i] += co[i];
}
if (bo != 0 && a_row_sum) {
for (dim_t i = 0; i < m; i++)
col_offset[i] -= bo * a_row_sum[i];
}
}
if (row_req) {
for (dim_t i = 0; i < n; i++)
row_offset[i] = 0;
if (offsetc == offset_type::row) {
for (dim_t i = 0; i < n; i++)
row_offset[i] += co[i];
}
if (ao != 0 && b_col_sum) {
for (dim_t i = 0; i < n; i++)
row_offset[i] -= ao * b_col_sum[i];
}
}
if (offsetc == offset_type::fixed && co_0 != 0) {
if (col_req) {
for (dim_t i = 0; i < m; i++)
col_offset[i] += co_0;
} else {
for (dim_t i = 0; i < n; i++)
row_offset[i] += co_0;
}
}
if (ao != 0 && bo != 0) {
if (col_req) {
for (dim_t i = 0; i < m; i++)
col_offset[i] += (c_type)k * ao * bo;
} else {
for (dim_t i = 0; i < n; i++)
row_offset[i] += (c_type)k * ao * bo;
}
}
}
if (m > 0 && n > 0)
gemm_kernel_8bit(m, n, k, alpha, const_cast<signed char *>(a),
const_cast<unsigned char *>(b), c, beta, ldc);
for (dim_t j = 0; j < n; j++) {
for (dim_t i = 0; i < m; i++) {
if (row_req) c[i + j * ldc] += row_offset[j];
if (col_req) c[i + j * ldc] += col_offset[i];
}
}
}
template <typename a_type, typename b_type, typename c_type>
static dnnl_status_t gemm_kernel_driver(int ithr, dim_t m, dim_t n, dim_t k,
const a_type *a, const b_type *b, float beta, c_type *c, dim_t ldc,
offset_type offsetc, const c_type *co,
const gemm_info_t<a_type, b_type, c_type> *arg) {
if (m <= 0 || n <= 0) return dnnl_success;
dim_t lda = arg->lda;
dim_t ldb = arg->ldb;
float alpha = arg->alpha;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
const std::shared_ptr<const gemm_pack_storage_t> &a_packed = arg->a_packed;
const std::shared_ptr<const gemm_pack_storage_t> &b_packed = arg->b_packed;
if (!is_int8 && beta != 1.0f && beta != 0.0f) { beta = 1.0f; }
if (!is_int8 && alpha == 0.0f) { return dnnl_success; }
dim_t k_padd = get_k_padd(ithr, k, arg);
dim_t m_padd = get_m_padd(ithr, m, arg);
dim_t n_padd = get_n_padd(ithr, n, k, arg);
dim_t ldc_buf = gemm_utils::get_ld_padd<c_type>(m_padd);
dim_t strideAm = (arg->transa == no_trans) ? 1 : lda;
dim_t strideAn = (arg->transa != no_trans) ? 1 : lda;
dim_t strideBm = (arg->transb == no_trans) ? 1 : ldb;
dim_t strideBn = (arg->transb != no_trans) ? 1 : ldb;
size_t a_buf_nelems = m_padd * k_padd;
size_t b_buf_nelems = k_padd * n_padd;
size_t a_row_sum_nelems = m_padd;
size_t b_col_sum_nelems = n_padd;
if (a_packed) a_buf_nelems = a_row_sum_nelems = 0;
if (b_packed) b_buf_nelems = b_col_sum_nelems = 0;
size_t mem_size = a_buf_nelems * sizeof(*a) + PAGE_4K
+ b_buf_nelems * sizeof(*b) + PAGE_4K;
if (is_int8) {
mem_size += a_row_sum_nelems * sizeof(*c) + PAGE_4K
+ b_col_sum_nelems * sizeof(*c) + PAGE_4K;
}
size_t col_offset_ws_nelems = arg->um;
size_t row_offset_ws_nelems = n_padd;
size_t stk_sz = (col_offset_ws_nelems + row_offset_ws_nelems) * sizeof(*c);
const bool use_stack = is_int8 && stk_sz <= MAX_STACK_SZ;
if (!use_stack) {
mem_size += col_offset_ws_nelems * sizeof(*c) + PAGE_4K;
mem_size += row_offset_ws_nelems * sizeof(*c) + PAGE_4K;
}
bool need_c_buffer
= (is_int8 && (alpha != 1.0f || (beta != 1.0f && beta != 0.0f)));
if (need_c_buffer) {
size_t c_buf_nelems = ldc_buf * n_padd;
mem_size += c_buf_nelems * sizeof(*c) + PAGE_4K;
}
char *mem = nullptr;
if (mem_size > 0) {
mem = (char *)malloc(mem_size, 128);
if (!mem) return dnnl_out_of_memory;
}
a_type *bufferA = (a_type *)align(mem, PAGE_4K);
void *p_next_buf = bufferA + a_buf_nelems;
uint8_t *bufferB = (uint8_t *)align(p_next_buf, PAGE_4K);
p_next_buf = bufferB + b_buf_nelems;
c_type *a_row_sum = nullptr;
c_type *b_col_sum = nullptr;
if (is_int8) {
a_row_sum = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = a_row_sum + a_row_sum_nelems;
b_col_sum = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = b_col_sum + b_col_sum_nelems;
}
c_type *col_offset_ws = nullptr;
c_type *row_offset_ws = nullptr;
if (!use_stack) {
col_offset_ws = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = col_offset_ws + col_offset_ws_nelems;
row_offset_ws = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = row_offset_ws + row_offset_ws_nelems;
}
c_type *bufferC = nullptr;
if (need_c_buffer) bufferC = (c_type *)align(p_next_buf, PAGE_4K);
int a_block_copied = 0;
dim_t sizeM = 0;
for (dim_t Bm = 0; Bm < m; Bm += sizeM) {
sizeM = m - Bm;
if (sizeM > m_padd) sizeM = m_padd;
dim_t sizeK = 0;
dim_t blk_k = 0;
for (dim_t Bk = 0; Bk < k; Bk += sizeK, blk_k++) {
sizeK = k - Bk;
if (sizeK > k_padd) sizeK = k_padd;
auto beta_eff = (Bk == 0) ? beta : 1.0f;
auto offsetc_eff = offset_type::none;
if (Bk + sizeK == k) offsetc_eff = offsetc;
dim_t sizeN = 0;
for (dim_t Bn = 0; Bn < n; Bn += sizeN) {
sizeN = n - Bn;
if (sizeN > n_padd) sizeN = n_padd;
if (b_packed) {
bufferB = b_packed->matrix<uint8_t>(ithr, Bk, Bn);
if (is_int8)
b_col_sum = b_packed->col_sums<c_type>(ithr, blk_k, Bn);
} else {
const b_type *b_block = b + Bk * strideBm + Bn * strideBn;
if (arg->transb) {
packB_T8_8bit<b_type>(sizeK, sizeN, b_block, ldb,
bufferB, arg->b_is_signed);
} else {
packB_N8bit<b_type>(sizeK, sizeN, b_block, ldb, bufferB,
arg->b_is_signed);
}
if (arg->ao != 0) {
if (arg->transb == 1) {
for (int i = 0; i < sizeN; i++) {
int sum = 0;
for (int j = 0; j < sizeK; j++) {
if (arg->b_is_signed) {
sum += b_block[j * ldb + i] + 128;
} else {
sum += b_block[j * ldb + i];
}
}
b_col_sum[i] = sum;
}
} else {
for (int i = 0; i < sizeN; i++) {
int sum = 0;
for (int j = 0; j < sizeK; j++) {
if (arg->b_is_signed) {
sum += b_block[i * ldb + j] + 128;
} else {
sum += b_block[i * ldb + j];
}
}
b_col_sum[i] = sum;
}
}
}
}
dim_t sizeUM = 0;
for (dim_t Um = 0; Um < sizeM; Um += sizeUM) {
sizeUM = sizeM - Um;
if (sizeUM > arg->um) sizeUM = arg->um;
dim_t Um_forA = 0;
if (sizeN < n) Um_forA = Um;
a_type *bufferA_eff = nullptr;
c_type *a_row_sum_eff = nullptr;
if (a_packed) {
Um_forA = Um;
dim_t buf_shift = 0;
buf_shift = Um_forA * sizeK;
bufferA_eff = a_packed->matrix<a_type>(ithr, Bm, Bk)
+ buf_shift;
if (is_int8)
a_row_sum_eff = a_packed->row_sums<c_type>(
ithr, Bm, blk_k)
+ Um_forA;
} else {
dim_t buf_shift = 0;
buf_shift = Um_forA * ((sizeK + 3) & (~3));
bufferA_eff = bufferA + buf_shift;
a_row_sum_eff
= a_row_sum ? a_row_sum + Um_forA : nullptr;
if (!a_block_copied) {
const a_type *a_block
= a + (Bm + Um) * strideAm + Bk * strideAn;
if (arg->transa) {
pack_N16_8bit_V2_lxvp<__vector signed char>(
sizeK, sizeUM, a_block, lda,
bufferA_eff, a_row_sum_eff);
} else {
pack_T16_8bit_V2<__vector signed char>(sizeK,
sizeUM, a_block, lda, bufferA_eff,
a_row_sum_eff);
}
}
}
c_type *c_block = c + (Bm + Um) + Bn * ldc;
dim_t co_stride = 0;
if (offsetc_eff == offset_type::row)
co_stride = Bn;
else if (offsetc_eff == offset_type::column)
co_stride = Bm + Um;
if (need_c_buffer) {
gemm_kernel(sizeUM, sizeN, sizeK, 1.0f, bufferA_eff,
bufferB, 0.0f, bufferC + Um, ldc_buf,
a_row_sum_eff, b_col_sum, row_offset_ws,
col_offset_ws, (c_type *)nullptr,
offset_type::none, arg);
add_results(sizeUM, sizeN, alpha, beta_eff,
bufferC + Um, ldc_buf, c_block, ldc,
co + co_stride, offsetc_eff);
} else {
gemm_kernel(sizeUM, sizeN, sizeK, alpha, bufferA_eff,
bufferB, beta_eff, c_block, ldc, a_row_sum_eff,
b_col_sum, row_offset_ws, col_offset_ws,
co + co_stride, offsetc_eff, arg);
}
}
a_block_copied = 1;
}
a_block_copied = 0;
}
}
free(mem);
return dnnl_success;
}
template <typename a_type, typename b_type, typename c_type>
static dnnl_status_t kernel_driver_parallel_acopiedbcopy(int ithr, dim_t m,
dim_t n, dim_t k, dim_t blk_k, dim_t Bk, const a_type *bufferA,
const b_type *b, float beta, c_type *c, offset_type offsetc,
const c_type *co, const c_type *a_row_sum,
const gemm_info_t<a_type, b_type, c_type> *arg) {
dim_t ldb = arg->ldb;
dim_t ldc = arg->ldc;
float alpha = arg->alpha;
const std::shared_ptr<const gemm_pack_storage_t> &b_packed = arg->b_packed;
if (m <= 0 || n <= 0) { return dnnl_success; }
dim_t n_padd = get_n_padd(ithr, n, k, arg);
dim_t ldc_buf = gemm_utils::get_ld_padd<c_type>(m);
dim_t strideBn = (arg->transb != 0) ? 1 : ldb;
size_t b_buf_nelems = k * n_padd;
size_t b_col_sum_nelems = n_padd;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
if (b_packed) b_buf_nelems = b_col_sum_nelems = 0;
size_t mem_size = b_buf_nelems * sizeof(*b) + PAGE_4K;
if (is_int8) { mem_size += b_col_sum_nelems * sizeof(*c) + PAGE_4K; }
size_t col_offset_ws_nelems = m;
size_t row_offset_ws_nelems = n_padd;
size_t stk_sz = (col_offset_ws_nelems + row_offset_ws_nelems) * sizeof(*c);
const bool use_stack = is_int8 && stk_sz <= MAX_STACK_SZ;
if (!use_stack) {
mem_size += col_offset_ws_nelems * sizeof(*c) + PAGE_4K;
mem_size += row_offset_ws_nelems * sizeof(*c) + PAGE_4K;
}
bool need_c_buffer
= (is_int8 && (alpha != 1.0f || (beta != 1.0f && beta != 0.0f)));
if (need_c_buffer) {
size_t c_buf_nelems = ldc_buf * n_padd;
mem_size += c_buf_nelems * sizeof(*c) + PAGE_4K;
}
char *mem = nullptr;
if (mem_size > 0) {
mem = (char *)malloc(mem_size, 128);
if (!mem) return dnnl_out_of_memory;
}
uint8_t *bufferB = (uint8_t *)align(mem, PAGE_4K);
void *p_next_buf = bufferB + b_buf_nelems;
c_type *b_col_sum = nullptr;
if (is_int8) {
b_col_sum = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = b_col_sum + b_col_sum_nelems;
}
c_type *col_offset_ws = nullptr;
c_type *row_offset_ws = nullptr;
if (!use_stack) {
col_offset_ws = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = col_offset_ws + col_offset_ws_nelems;
row_offset_ws = (c_type *)align(p_next_buf, PAGE_4K);
p_next_buf = row_offset_ws + row_offset_ws_nelems;
}
c_type *bufferC = nullptr;
if (need_c_buffer) bufferC = (c_type *)align(p_next_buf, PAGE_4K);
dim_t sizeN = 0;
for (dim_t Bn = 0; Bn < n; Bn += sizeN) {
sizeN = n - Bn;
if (sizeN > n_padd) sizeN = n_padd;
if (b_packed) {
bufferB = b_packed->matrix<uint8_t>(ithr, Bk, Bn);
if (is_int8)
b_col_sum = b_packed->col_sums<c_type>(ithr, blk_k, Bn);
} else {
const b_type *b_block = b + Bn * strideBn;
if (arg->transb) {
packB_T8_8bit(
k, sizeN, b_block, ldb, bufferB, arg->b_is_signed);
} else {
packB_N8bit<b_type>(
k, sizeN, b_block, ldb, bufferB, arg->b_is_signed);
}
if (arg->ao != 0) {
if (arg->transb == 1) {
for (int i = 0; i < sizeN; i++) {
int sum = 0;
for (int j = 0; j < k; j++) {
if (arg->b_is_signed) {
sum += b_block[j * ldb + i] + 128;
} else {
sum += b_block[j * ldb + i];
}
}
b_col_sum[i] = sum;
}
} else {
for (int i = 0; i < sizeN; i++) {
int sum = 0;
for (int j = 0; j < k; j++) {
if (arg->b_is_signed) {
sum += b_block[i * ldb + j] + 128;
} else {
sum += b_block[i * ldb + j];
}
}
b_col_sum[i] = sum;
}
}
}
}
dim_t co_stride = 0;
if (offsetc == offset_type::fixed) {
co_stride = 0;
} else if (offsetc == offset_type::row) {
co_stride = Bn;
} else if (offsetc == offset_type::column) {
co_stride = 0;
}
c_type *c_block = c + Bn * ldc;
if (need_c_buffer) {
gemm_kernel(m, sizeN, k, 1.0f, bufferA, bufferB, 0.0f, bufferC,
ldc_buf, a_row_sum, b_col_sum, row_offset_ws, col_offset_ws,
(c_type *)nullptr, offset_type::none, arg);
add_results(m, sizeN, alpha, beta, bufferC, ldc_buf, c_block, ldc,
co + co_stride, offsetc);
} else {
gemm_kernel(m, sizeN, k, alpha, bufferA, bufferB, beta, c_block,
ldc, a_row_sum, b_col_sum, row_offset_ws, col_offset_ws,
co + co_stride, offsetc, arg);
}
}
free(mem);
return dnnl_success;
}
template <typename a_type, typename b_type, typename c_type>
static inline void set_thread_opts_nopack(int nthrs, int nthrs_spawn,
gemm_threading_t &thread_info,
const gemm_info_t<a_type, b_type, c_type> *arg) {
static constexpr dim_t N2D_MAX = 384;
static constexpr dim_t M2D_MIN = 384;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
dim_t m = arg->m;
dim_t n = arg->n;
dim_t k = arg->k;
thread_info.nthrs_m = 0;
thread_info.nthrs_n = 0;
thread_info.nthrs_k = 0;
thread_info.copy = copy_type::nonshared;
thread_info.partition = partition_type::row_1d;
thread_info.nthrs_k = 1;
thread_info.thread_k = k;
bool condition_2D_bsrc = false;
int scale = nthrs;
condition_2D_bsrc = (256 * m > scale * n) && (scale * m < 256 * n);
int condition_1D_copya = false;
if (m >= 1000 && n >= 4000) {
condition_2D_bsrc = false;
condition_1D_copya = true;
}
if (is_int8 && arg->ao != 0 && (arg->bo != 0)) {
condition_2D_bsrc = false;
condition_1D_copya = true;
}
if (condition_2D_bsrc) {
int nthrs_m = 1;
int nthrs_n = nthrs;
if (m == 800 && n == 300) {
auto &thread_m = thread_info.thread_m;
auto &thread_n = thread_info.thread_n;
const dim_t block_m = arg->um * 4;
constexpr dim_t block_n = 64;
constexpr dim_t small_m = 16;
constexpr dim_t small_n = 2;
std::tie(nthrs_m, nthrs_n) = gemm_utils::calc_nthr_2d(nthrs, m, n,
block_m, block_n, small_m, small_n, thread_m, thread_n);
thread_info.nthrs_m = nthrs_m;
thread_info.nthrs_n = nthrs_n;
thread_info.partition = partition_type::mnk_3d;
} else if ((n <= 64 || n >= 256)) {
while (((nthrs_n > 1) && (n / nthrs_n < arg->un)
&& (m / nthrs_m >= 2 * arg->um))
|| ((nthrs_n % 2 == 0)
&& (n / nthrs > N2D_MAX
|| n / nthrs_n <= N2D_MAX / 2)
&& (m / nthrs_m >= 2 * M2D_MIN) && (nthrs_m < 4))) {
nthrs_m *= 2;
nthrs_n /= 2;
}
thread_info.nthrs_m = nthrs_m;
thread_info.nthrs_n = nthrs_n;
thread_info.partition = partition_type::col_major_2d;
} else {
set_thread_opts_pack(nthrs, thread_info, arg, false);
}
} else if (condition_1D_copya && dnnl_thr_syncable()) {
thread_info.copy = copy_type::shared_a;
thread_info.partition = partition_type::col_1d;
thread_info.nthrs_m = 1;
thread_info.nthrs_n = nthrs_spawn; } else {
auto veclen = get_vector_length<c_type>();
if (m > n && (m >= nthrs * veclen || n < nthrs)) {
if (n <= 20 && is_int8) {
set_thread_opts_pack(
nthrs, thread_info, arg, false, true, false);
} else {
thread_info.partition = partition_type::row_1d;
thread_info.nthrs_m = nthrs;
thread_info.nthrs_n = 1;
}
} else {
thread_info.partition = partition_type::col_1d;
thread_info.nthrs_m = 1;
thread_info.nthrs_n = nthrs;
}
}
}
template <typename a_type, typename b_type, typename c_type>
static inline void set_thread_opts_pack(int nthrs,
gemm_threading_t &thread_info,
const gemm_info_t<a_type, b_type, c_type> *arg,
bool do_k_blocking = true, bool do_m_blocking = true,
bool do_n_blocking = true) {
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
constexpr bool is_bf16
= data_traits_t<a_type>::data_type == data_type::bf16;
bool do_m_blocking_only = do_m_blocking && !do_n_blocking;
auto m = arg->m, n = arg->n, k = arg->k;
auto &nthr_m = thread_info.nthrs_m;
auto &nthr_n = thread_info.nthrs_n;
auto &nthr_k = thread_info.nthrs_k;
auto &thread_m = thread_info.thread_m;
auto &thread_n = thread_info.thread_n;
auto &thread_k = thread_info.thread_k;
auto &block_m = thread_info.block_m;
auto &block_n = thread_info.block_n;
auto &block_k = thread_info.block_k;
constexpr auto MBLK = 64;
constexpr auto NBLK = 64;
auto KBLK = is_int8 ? 3072 : 256;
KBLK = do_m_blocking_only && is_int8 ? 384 : KBLK;
nthr_m = nthr_n = nthr_k = 1;
thread_info.copy = copy_type::nonshared;
thread_info.partition = partition_type::mnk_3d;
auto choose_blocking
= [](dim_t size_z, dim_t &thread_z, int &nthr_z, dim_t block_z_init,
dim_t &block_z, dim_t block_align) {
thread_z = utils::div_up(size_z, nthr_z);
auto num_blk = utils::div_up(thread_z, block_z_init);
block_z = utils::div_up(thread_z, num_blk);
block_z = utils::rnd_up(block_z, block_align);
thread_z = num_blk * block_z;
if (thread_z * nthr_z > size_z)
nthr_z = utils::div_up(size_z, thread_z);
};
auto choose_m_blocking = [&]() {
auto align = get_vector_length<c_type>();
align = do_m_blocking_only ? arg->um : align;
choose_blocking(m, thread_m, nthr_m, arg->bm, block_m, align);
};
auto choose_n_blocking = [&]() {
choose_blocking(n, thread_n, nthr_n, arg->bn, block_n, arg->un);
};
auto choose_k_blocking = [&]() {
auto align = nstl::max(arg->uk, dim_t(4));
choose_blocking(k, thread_k, nthr_k, arg->bk, block_k, align);
};
if ((m / MBLK + n / NBLK) < nthrs && do_k_blocking) {
for (int nk = 1; nk <= 4 && k >= ((KBLK + 1) * nk); nk++)
if (nthrs % nk == 0) nthr_k = nk;
if (nthr_k == 1 && nthrs > 1 && do_m_blocking_only) {
nthrs--;
for (int nk = 1; nk <= 4 && k >= ((KBLK + 1) * nk); nk++)
if (nthrs % nk == 0) nthr_k = nk;
}
if (nthr_k < 4 && k >= m * 4 && k >= n * 4 && nthrs > 10 && is_bf16) {
for (int nk = 1; nk <= 4 && k >= ((KBLK + 1) * nk); nk++)
if (nthrs % nk <= 2) nthr_k = nk;
}
}
choose_k_blocking();
auto min_mblk = arg->um;
min_mblk = do_m_blocking ? min_mblk : m;
min_mblk = do_m_blocking_only ? arg->um : min_mblk;
auto min_nblk = do_n_blocking ? NBLK / 2 : n;
std::tie(nthr_m, nthr_n) = partition_2d_minblk(m, n, MBLK, NBLK, min_mblk,
min_nblk, arg->um, arg->un, nthrs / nthr_k,
do_m_blocking && do_n_blocking && do_k_blocking);
auto nthr_m_init = nthr_m, nthr_n_init = nthr_n;
choose_m_blocking();
choose_n_blocking();
if (is_int8 && do_m_blocking && do_n_blocking) {
if ((nthr_n != nthr_n_init)
&& ((nthr_m + 1) * nthr_n * nthr_k <= nthrs)) {
nthr_m++;
choose_m_blocking();
}
if ((nthr_m != nthr_m_init)
&& (nthr_m * (nthr_n + 1) * nthr_k <= nthrs)) {
nthr_n++;
choose_n_blocking();
}
}
}
template <typename a_type, typename b_type, typename c_type>
static inline int set_thread_opts(int nthrs, int nthrs_spawn,
gemm_threading_t &thread_info,
const gemm_info_t<a_type, b_type, c_type> *arg) {
thread_info.block_m = thread_info.block_n = thread_info.block_k = -1;
thread_info.thread_m = thread_info.thread_n = thread_info.thread_k = -1;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
if (arg->packing != pack_type::none && (is_int8))
set_thread_opts_pack(nthrs, thread_info, arg);
else
set_thread_opts_nopack(nthrs, nthrs_spawn, thread_info, arg);
return thread_info.nthrs_m * thread_info.nthrs_n * thread_info.nthrs_k;
}
template <typename a_type, typename b_type, typename c_type>
static inline std::tuple<const a_type *, const b_type *, c_type *,
const c_type *>
decompose_matrices(const gemm_slice_t &slice,
const gemm_info_t<a_type, b_type, c_type> *arg) {
dim_t stride_am = (arg->transa == no_trans) ? 1 : arg->lda;
dim_t stride_ak = (arg->transa != no_trans) ? 1 : arg->lda;
dim_t stride_bn = (arg->transb != no_trans) ? 1 : arg->ldb;
dim_t stride_bk = (arg->transb == no_trans) ? 1 : arg->ldb;
auto a = arg->a;
auto b = arg->b;
auto c = arg->c;
if (a) a += slice.off_m * stride_am + slice.off_k * stride_ak;
if (b) b += slice.off_n * stride_bn + slice.off_k * stride_bk;
if (c) c += slice.off_m + slice.off_n * arg->ldc;
dim_t co_stride;
switch (arg->offsetc) {
case offset_type::row: co_stride = slice.off_n; break;
case offset_type::column: co_stride = slice.off_m; break;
default: co_stride = 0; break;
}
auto co = arg->co;
if (co) co += co_stride;
return std::make_tuple(a, b, c, co);
}
template <typename a_type, typename b_type, typename c_type>
static dnnl_status_t parallel_a_copy(const int ithr, const int nthrs,
const dim_t m, const dim_t n, const dim_t k, const a_type *a,
const b_type *b, float beta, c_type *c, dim_t ldc, offset_type offsetc,
const c_type *co, const gemm_info_t<a_type, b_type, c_type> *arg,
char **p_shared_mem) {
const dim_t lda = arg->lda;
const dim_t ldb = arg->ldb;
const dim_t strideAm = (arg->transa == no_trans) ? 1 : lda;
const dim_t strideAn = (arg->transa != no_trans) ? 1 : lda;
const dim_t strideBm = (arg->transb == no_trans) ? 1 : ldb;
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
const std::shared_ptr<const gemm_pack_storage_t> &a_packed = arg->a_packed;
dim_t m_padd = get_m_padd_parallel_a(ithr, m, arg, nthrs);
dim_t k_padd = get_k_padd(ithr, k, arg);
size_t a_buf_nelems = m_padd * k_padd;
char *mem = nullptr;
a_type *bufferA = nullptr;
c_type *a_row_sum = nullptr;
if (!a_packed) {
if (ithr == 0) { size_t mem_size = (a_buf_nelems * sizeof(*a) + PAGE_4K);
if (is_int8) {
size_t a_row_sum_nelems = m_padd;
mem_size += a_row_sum_nelems * sizeof(*c) + PAGE_4K;
}
*p_shared_mem = (char *)malloc(mem_size, 128);
}
dnnl_thr_barrier();
mem = *p_shared_mem;
bufferA = (a_type *)align(mem, PAGE_4K);
if (is_int8)
a_row_sum = (c_type *)align(bufferA + a_buf_nelems, PAGE_4K);
if (!mem) return dnnl_out_of_memory;
}
dnnl_status_t result = dnnl_success;
dim_t sizeK = 0;
dim_t blk_k = 0;
for (dim_t Bk = 0; Bk < k; Bk += sizeK, blk_k++) {
sizeK = k - Bk;
if (sizeK > k_padd) sizeK = k_padd;
auto beta_eff = (Bk == 0) ? beta : 1.0f;
auto offsetc_eff = offset_type::none;
if (Bk + sizeK == k) offsetc_eff = offsetc;
dim_t sizeM = 0;
for (dim_t Bm = 0; Bm < m; Bm += sizeM) {
sizeM = m - Bm;
if (sizeM > m_padd) sizeM = m_padd;
if ((ithr < nthrs) && !a_packed) {
dim_t band = (sizeM + nthrs - 1) / nthrs;
band = utils::rnd_up(band, arg->um);
dim_t offset = band * ithr;
if (offset >= sizeM) {
offset = 0;
band = 0;
}
if (offset + band > sizeM) { band = sizeM - offset; }
if (band > 0) {
const a_type *a_block
= a + (Bm + offset) * strideAm + Bk * strideAn;
dim_t buf_shift = 0;
buf_shift = offset * ((sizeK + 3) & ~3);
c_type *a_row_sum_eff
= a_row_sum ? a_row_sum + offset : nullptr;
if (arg->transa) {
pack_N16_8bit_V2_lxvp<__vector signed char>(sizeK, band,
a_block, lda, bufferA + buf_shift,
a_row_sum_eff);
} else {
pack_T16_8bit_V2<__vector signed char>(sizeK, band,
a_block, lda, bufferA + buf_shift,
a_row_sum_eff);
}
}
}
if (!a_packed)
dnnl_thr_barrier();
const b_type *b_block = b + Bk * strideBm;
c_type *c_block = c + Bm;
dim_t co_stride = 0;
if (offsetc_eff == offset_type::fixed) {
co_stride = 0;
} else if (offsetc_eff == offset_type::row) {
co_stride = 0;
} else if (offsetc_eff == offset_type::column) {
co_stride = Bm;
}
auto bufferA_eff
= a_packed ? a_packed->matrix<a_type>(0, Bm, Bk) : bufferA;
auto a_row_sum_eff = a_packed
? a_packed->row_sums<c_type>(0, Bm, blk_k)
: a_row_sum;
auto this_result = kernel_driver_parallel_acopiedbcopy(ithr, sizeM,
n, sizeK, blk_k, Bk, bufferA_eff, b_block, beta_eff,
c_block, offsetc_eff, co + co_stride, a_row_sum_eff, arg);
if (this_result != dnnl_success) result = this_result;
if (!a_packed)
dnnl_thr_barrier(); }
}
if (ithr == 0 && !a_packed) free(mem);
return result;
}
template <typename T>
static inline void adjust_thread_count(dim_t m, dim_t n, dim_t k, int *nthrs) {
const double omp_overhead_small_core = 3.0e+3;
const double omp_intercept_big_core = 4.0e+3;
const double omp_slope_big_core = 5.0e+2;
auto veclen = get_vector_length<T>();
const double fp_per_cycle = 2.0 * 2.0 * veclen;
double gemm_cycles = m * n * k / fp_per_cycle;
gemm_cycles *= 8.0;
int i = *nthrs;
if (*nthrs <= 4 && omp_overhead_small_core > 0) {
double omp_cycles = omp_overhead_small_core;
if (gemm_cycles < omp_cycles) {
*nthrs = 1;
return;
} else {
while (i > 1) {
if (omp_cycles * i < gemm_cycles * (i - 1)) break;
--i;
}
}
} else {
if (gemm_cycles < (omp_intercept_big_core + 2 * omp_slope_big_core)) {
*nthrs = 1;
return;
}
while (i > 1) {
double omp_cycles = omp_intercept_big_core + i * omp_slope_big_core;
if (omp_cycles * i < gemm_cycles * (i - 1)) break;
if (i < 10)
i -= 2;
else if (i < 30)
i -= 4;
else
i -= 8;
}
}
if (i < 1) i = 1;
*nthrs = i;
}
template <typename a_type, typename b_type, typename c_type>
static dnnl_status_t gemm_threading_driver(
gemm_info_t<a_type, b_type, c_type> *arg) {
auto packing = (arg->packing != pack_type::none);
auto is_a_packed = (arg->transa == packed);
auto is_b_packed = (arg->transb == packed);
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
if ((arg->m <= 0) || (arg->n <= 0)) return dnnl_success;
if (is_a_packed && arg->bo != 0)
if (!arg->a_packed->has_row_sums()) return dnnl_invalid_arguments;
if (is_b_packed && arg->ao != 0)
if (!arg->b_packed->has_col_sums()) return dnnl_invalid_arguments;
auto nthr_max = dnnl_get_current_num_threads();
int nthr_goal = nthr_max;
adjust_thread_count<c_type>(arg->m, arg->n, arg->k, &nthr_goal);
const gemm_threading_t *force_threading = nullptr;
gemm_threading_t force_k_decomp;
if (!packing) {
if (is_a_packed && is_b_packed)
if (arg->a_packed->threading() != arg->b_packed->threading())
return dnnl_invalid_arguments;
if (is_a_packed)
force_threading = &arg->a_packed->threading();
else if (is_b_packed)
force_threading = &arg->b_packed->threading();
else if (arg->n <= 128 && arg->k >= 3072 && is_int8) {
set_thread_opts_pack(nthr_goal, force_k_decomp, arg);
if (force_k_decomp.nthrs_k > 1) force_threading = &force_k_decomp;
}
if (force_threading) {
nthr_goal = force_threading->nthrs();
arg->update_blocking(*force_threading);
}
} else {
gemm_pack_storage_t *pack_dst = arg->pack_dst;
bool do_a = (arg->packing == pack_type::pack_a);
pack_dst->which() = do_a ? matrix_id::a : matrix_id::b;
pack_dst->setup(nthr_goal, do_a && is_int8, !do_a && is_int8);
auto &thread_info = pack_dst->threading();
force_threading = &thread_info;
nthr_goal = set_thread_opts(nthr_goal, nthr_max, thread_info, arg);
arg->update_blocking(thread_info);
if (thread_info.copy != copy_type::no_copy) {
for (int ithr = 0; ithr < nthr_goal; ithr++) {
if (!pack_dst->is_first_thread_in_slice(ithr)) continue;
auto slice = thread_info.get_thread_slice(
ithr, arg->m, arg->n, arg->k);
auto m = slice.m, n = slice.n, k = slice.k;
auto m_padd = (thread_info.copy == copy_type::shared_a)
? get_m_padd_parallel_a(
ithr, m, arg, thread_info.nthrs())
: get_m_padd(ithr, m, arg);
auto n_padd = get_n_padd(ithr, n, k, arg);
auto k_padd = get_k_padd(ithr, k, arg);
do_a ? pack_dst->set_blocking(ithr, m, k, m_padd, k_padd)
: pack_dst->set_blocking(ithr, k, n, k_padd, n_padd);
}
} else {
auto ld = do_a ? gemm_utils::get_ld_padd<a_type>(arg->m)
: gemm_utils::get_ld_padd<b_type>(arg->k);
pack_dst->set_nocopy(0, no_trans, ld, do_a ? arg->k : arg->n);
}
do_a ? pack_dst->finalize<a_type, c_type>()
: pack_dst->finalize<b_type, c_type>();
if (arg->measure_only) return dnnl_success;
}
if (nthr_goal == 1)
return gemm_kernel_driver(0, arg->m, arg->n, arg->k, arg->a, arg->b,
arg->beta, arg->c, arg->ldc, arg->offsetc, arg->co, arg);
bool k_blocking = force_threading && (force_threading->nthrs_k > 1);
bool k_summing = k_blocking && !packing;
auto *thread_arg = (gemm_per_thread_t<c_type> *)malloc(
sizeof(gemm_per_thread_t<c_type>) * nthr_max, PAGE_4K);
if (!thread_arg) return dnnl_out_of_memory;
dim_t max_mt = 0, max_nt = 0;
for (int ithr = 0; ithr < nthr_max; ithr++) {
thread_arg[ithr].result = dnnl_success;
thread_arg[ithr].compute_done = false;
thread_arg[ithr].c_local = nullptr;
thread_arg[ithr].c_global = nullptr;
thread_arg[ithr].ldc_global = arg->ldc;
thread_arg[ithr].ldc_local = 0;
if (force_threading) {
thread_arg[ithr].slice = force_threading->get_thread_slice(
ithr, arg->m, arg->n, arg->k);
thread_arg[ithr].nthr_k = force_threading->nthrs_k;
thread_arg[ithr].thr_k_stride = force_threading->thr_k_stride();
max_mt = nstl::max(max_mt, thread_arg[ithr].slice.m);
max_nt = nstl::max(max_nt, thread_arg[ithr].slice.n);
} else {
thread_arg[ithr].slice = {0, 0, 0, 0, 0, 0, 0, 0, 0};
thread_arg[ithr].nthr_k = 1;
thread_arg[ithr].thr_k_stride = 0;
}
}
c_type *c_local_storage = nullptr;
if (k_summing) {
const dim_t BAD_LD_MULT = 256;
dim_t ldc_local = max_mt % BAD_LD_MULT
? max_mt
: gemm_utils::get_ld_padd<c_type>(max_mt);
dim_t c_local_stride = ldc_local * max_nt;
c_local_storage = (c_type *)malloc(
sizeof(c_type) * c_local_stride * nthr_goal, PAGE_4K);
if (!c_local_storage) {
free(thread_arg);
return dnnl_out_of_memory;
}
for (int ithr = 0; ithr < nthr_goal; ithr++) {
thread_arg[ithr].c_local = c_local_storage + ithr * c_local_stride;
thread_arg[ithr].ldc_local = ldc_local;
}
}
char *shared_mem = nullptr;
int nthr_spawn = dnnl_thr_syncable() ? nthr_max : nthr_goal;
parallel(nthr_spawn, [&](int ithr, int nthr) {
int nthr_eff = force_threading ? nthr_goal : nstl::min(nthr_goal, nthr);
if (nthr_eff == 1) {
thread_arg[0].result = gemm_kernel_driver(0, arg->m, arg->n, arg->k,
arg->a, arg->b, arg->beta, arg->c, arg->ldc, arg->offsetc,
arg->co, arg);
} else {
gemm_threading_t thread_info;
if (force_threading)
thread_info = *force_threading;
else {
nthr_eff = set_thread_opts(nthr_eff, nthr, thread_info, arg);
if (ithr < nthr_eff)
thread_arg[ithr].slice = thread_info.get_thread_slice(
ithr, arg->m, arg->n, arg->k);
}
for (; ithr < nthr_eff; ithr += nthr) {
const a_type *a = nullptr;
const b_type *b = nullptr;
c_type *c = nullptr;
const c_type *co = nullptr;
std::tie(a, b, c, co)
= decompose_matrices(thread_arg[ithr].slice, arg);
auto m = thread_arg[ithr].slice.m;
auto n = thread_arg[ithr].slice.n;
auto k = thread_arg[ithr].slice.k;
thread_arg[ithr].c_global = c;
auto c_eff = c;
auto ldc_eff = arg->ldc;
auto beta_eff = arg->beta;
auto offsetc_eff = arg->offsetc;
if (k_summing && thread_arg[ithr].slice.ithr_k > 0) {
c_eff = thread_arg[ithr].c_local;
ldc_eff = thread_arg[ithr].ldc_local;
beta_eff = 0;
offsetc_eff = offset_type::none;
}
switch (thread_info.copy) {
case copy_type::shared_a:
thread_arg[ithr].result = parallel_a_copy(ithr,
nthr_eff, m, n, k, a, b, beta_eff, c_eff,
ldc_eff, offsetc_eff, co, arg, &shared_mem);
break;
default:
case copy_type::nonshared:
thread_arg[ithr].result = gemm_kernel_driver(ithr, m, n,
k, a, b, beta_eff, c_eff, ldc_eff, offsetc_eff,
co, arg);
break;
}
#if DNNL_THR_SYNC == 1
if (k_summing && (nthr >= nthr_eff)) {
thread_arg[ithr].compute_done = true;
sum_k_blocks(ithr, thread_arg, true);
}
#endif
}
}
});
dnnl_status_t result = dnnl_success; for (int ithr = 0; ithr < nthr_max; ithr++) {
if (thread_arg[ithr].result != dnnl_success) {
result = static_cast<dnnl_status_t>(thread_arg[ithr].result);
break;
}
}
if (k_summing && !thread_arg[0].compute_done) {
parallel(nthr_goal, [&](int ithr, int nthr) {
for (; ithr < nthr_goal; ithr += nthr)
sum_k_blocks(ithr, thread_arg, false);
});
}
if (c_local_storage) dnnl::impl::free(c_local_storage);
dnnl::impl::free(thread_arg);
return result;
}
template <typename a_type, typename b_type, typename c_type>
dnnl_status_t gemm_driver(const char *transA, const char *transB,
const char *offsetC, const dim_t *m, const dim_t *n, const dim_t *k,
const float *alpha, const a_type *a, const dim_t *lda, const a_type *oa,
const b_type *b, const dim_t *ldb, const b_type *ob, const float *beta,
c_type *c, const dim_t *ldc, const c_type *oc, const bool force_nocopy,
pack_type packing, gemm_pack_storage_t *pack_dst, bool measure_only) {
constexpr bool is_int8 = utils::one_of(
data_traits_t<a_type>::data_type, data_type::s8, data_type::u8);
MAYBE_UNUSED(is_int8);
gemm_info_t<a_type, b_type, c_type> args(transA, transB, offsetC, m, n, k,
alpha, a, lda, oa, b, ldb, ob, beta, c, ldc, oc, force_nocopy,
packing, pack_dst, measure_only);
return gemm_threading_driver(&args);
}
template dnnl_status_t
gemm_driver<int8_t, int8_t, int32_t>(const char *transA,
const char *transB, const char *offsetC, const dim_t *m,
const dim_t *n, const dim_t *k, const float *alpha,
const int8_t *a, const dim_t *lda, const int8_t *oa,
const int8_t *b, const dim_t *ldb, const int8_t *ob,
const float *beta, int32_t *c, const dim_t *ldc,
const int32_t *oc, const bool force_nocopy, pack_type packing,
gemm_pack_storage_t *pack_dst, bool measure_only);
template dnnl_status_t
gemm_driver<int8_t, uint8_t, int32_t>(const char *transA,
const char *transB, const char *offsetC, const dim_t *m,
const dim_t *n, const dim_t *k, const float *alpha,
const int8_t *a, const dim_t *lda, const int8_t *oa,
const uint8_t *b, const dim_t *ldb, const uint8_t *ob,
const float *beta, int32_t *c, const dim_t *ldc,
const int32_t *oc, const bool force_nocopy, pack_type packing,
gemm_pack_storage_t *pack_dst, bool measure_only);
#undef MAX_STACK_SZ
} } } } #endif