/******************************************************************************* * Copyright 2021 Intel Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *******************************************************************************/ #include #include "oneapi/dnnl/dnnl.h" #if DNNL_CPU_RUNTIME == DNNL_RUNTIME_THREADPOOL #include "oneapi/dnnl/dnnl_threadpool.h" #include "oneapi/dnnl/dnnl_threadpool_iface.hpp" #endif #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE #include "cpu/gemm/gemm.hpp" #endif #include "common/bfloat16.hpp" #include "common/c_types_map.hpp" #include "common/dnnl_thread.hpp" #include "common/profiler.hpp" #include "common/stack_checker.hpp" #include "common/verbose.hpp" using namespace dnnl::impl; #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE namespace { const char *c2f_offsetC(const char *offC) { if (offC) { if (offC[0] == 'R' || offC[0] == 'r') return "C"; if (offC[0] == 'C' || offC[0] == 'c') return "R"; } return offC; } std::string get_descriptor(dim_t M, dim_t N, dim_t K) { std::string s_ = std::to_string(M); s_ += "x"; s_ += std::to_string(K); s_ += ":"; s_ += std::to_string(K); s_ += "x"; s_ += std::to_string(N); return s_; } } // namespace #endif #ifdef DNNL_ENABLE_STACK_CHECKER #define MAYBE_RUN_STACK_CHECKER(api_name, ...) \ stack_checker::stack_checker_t(#api_name).check(__VA_ARGS__) #else #define MAYBE_RUN_STACK_CHECKER(_, func, ...) func(__VA_ARGS__) #endif #define MAYBE_VERBOSE(status, sdt_, wdt_, ddt_, ...) \ if (get_verbose(verbose_t::exec_profile, component_t::gemm_api)) { \ double start_ms = get_msec(); \ status = __VA_ARGS__; \ double duration_ms = get_msec() - start_ms; \ stringstream_t ss; \ ss << "cpu,gemm_api,,undef,"; \ const bool is_src_ab = (transa == 'N' || transa == 'n'); \ ss << "src_" << sdt_ << "::blocked:" << (is_src_ab ? "ab" : "ba") \ << ":f0 "; \ const bool is_wei_ab = (transb == 'N' || transb == 'n'); \ ss << "wei_" << wdt_ << "::blocked:" << (is_wei_ab ? "ab" : "ba") \ << ":f0 "; \ ss << "dst_" << ddt_ << "::blocked:ab:f0,"; \ if (is_src_ab && lda != K) ss << "lda:" << lda << " "; \ if (!is_src_ab && lda != M) ss << "lda:" << lda << " "; \ if (is_wei_ab && ldb != N) ss << "ldb:" << ldb << " "; \ if (!is_wei_ab && ldb != K) ss << "ldb:" << ldb << " "; \ if (alpha != 1.f) ss << "attr-scales:src:common:" << alpha << " "; \ if (beta != 0.f) ss << "attr-post-ops:sum:" << beta << " "; \ ss << ",," << get_descriptor(M, N, K); \ VPROF(start_ms, primitive, exec, VERBOSE_profile, ss.str().c_str(), \ duration_ms); \ } else { \ status = __VA_ARGS__; \ } dnnl_status_t dnnl_sgemm(char transa, char transb, dim_t M, dim_t N, dim_t K, float alpha, const float *A, dim_t lda, const float *B, const dim_t ldb, float beta, float *C, dim_t ldc) { #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE status_t status = dnnl_success; MAYBE_VERBOSE(status, "f32", "f32", "f32", MAYBE_RUN_STACK_CHECKER(dnnl_sgemm, cpu::extended_sgemm, &transb, &transa, &N, &M, &K, &alpha, B, &ldb, A, &lda, &beta, C, &ldc, nullptr, false)); return status; #else return dnnl::impl::status::unimplemented; #endif } dnnl_status_t dnnl_gemm_u8s8s32(char transa, char transb, char offsetc, dim_t M, dim_t N, dim_t K, float alpha, const uint8_t *A, dim_t lda, uint8_t ao, const int8_t *B, dim_t ldb, int8_t bo, float beta, int32_t *C, dim_t ldc, const int32_t *co) { #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE status_t status = dnnl_success; MAYBE_VERBOSE(status, "u8", "s8", "s32", MAYBE_RUN_STACK_CHECKER(dnnl_gemm_u8s8s32, cpu::gemm_s8u8s32, &transb, &transa, c2f_offsetC(&offsetc), &N, &M, &K, &alpha, B, &ldb, &bo, A, &lda, &ao, &beta, C, &ldc, co)); return status; #else return dnnl::impl::status::unimplemented; #endif } dnnl_status_t dnnl_gemm_s8s8s32(char transa, char transb, char offsetc, dim_t M, dim_t N, dim_t K, float alpha, const int8_t *A, dim_t lda, int8_t ao, const int8_t *B, dim_t ldb, int8_t bo, float beta, int32_t *C, dim_t ldc, const int32_t *co) { #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE status_t status = dnnl_success; MAYBE_VERBOSE(status, "s8", "s8", "s32", MAYBE_RUN_STACK_CHECKER(dnnl_gemm_s8s8s32, cpu::gemm_s8s8s32, &transb, &transa, c2f_offsetC(&offsetc), &N, &M, &K, &alpha, B, &ldb, &bo, A, &lda, &ao, &beta, C, &ldc, co)); return status; #else return dnnl::impl::status::unimplemented; #endif } extern "C" dnnl_status_t DNNL_API dnnl_gemm_bf16bf16f32(char transa, char transb, dim_t M, dim_t N, dim_t K, float alpha, const bfloat16_t *A, dim_t lda, const bfloat16_t *B, dim_t ldb, float beta, float *C, dim_t ldc) { #if DNNL_CPU_RUNTIME != DNNL_RUNTIME_NONE status_t status = dnnl_success; MAYBE_VERBOSE(status, "bf16", "bf16", "f32", MAYBE_RUN_STACK_CHECKER(dnnl_gemm_bf16bf16f32, cpu::gemm_bf16bf16f32, &transb, &transa, &N, &M, &K, &alpha, B, &ldb, A, &lda, &beta, C, &ldc)); return status; #else return dnnl::impl::status::unimplemented; #endif } #if DNNL_CPU_RUNTIME == DNNL_RUNTIME_THREADPOOL dnnl_status_t dnnl_threadpool_interop_sgemm(char transa, char transb, dim_t M, dim_t N, dim_t K, float alpha, const float *A, dim_t lda, const float *B, const dim_t ldb, float beta, float *C, dim_t ldc, void *th) { threadpool_utils::activate_threadpool( (dnnl::threadpool_interop::threadpool_iface *)th); status_t status = dnnl_success; MAYBE_VERBOSE(status, "f32", "f32", "f32", MAYBE_RUN_STACK_CHECKER(dnnl_threadpool_interop_sgemm, cpu::extended_sgemm, &transb, &transa, &N, &M, &K, &alpha, B, &ldb, A, &lda, &beta, C, &ldc, nullptr, false)); threadpool_utils::deactivate_threadpool(); return status; } dnnl_status_t dnnl_threadpool_interop_gemm_u8s8s32(char transa, char transb, char offsetc, dim_t M, dim_t N, dim_t K, float alpha, const uint8_t *A, dim_t lda, uint8_t ao, const int8_t *B, dim_t ldb, int8_t bo, float beta, int32_t *C, dim_t ldc, const int32_t *co, void *th) { threadpool_utils::activate_threadpool( (dnnl::threadpool_interop::threadpool_iface *)th); status_t status = dnnl_success; MAYBE_VERBOSE(status, "u8", "s8", "s32", MAYBE_RUN_STACK_CHECKER(dnnl_threadpool_interop_gemm_u8s8s32, cpu::gemm_s8u8s32, &transb, &transa, c2f_offsetC(&offsetc), &N, &M, &K, &alpha, B, &ldb, &bo, A, &lda, &ao, &beta, C, &ldc, co)); threadpool_utils::deactivate_threadpool(); return status; } dnnl_status_t dnnl_threadpool_interop_gemm_s8s8s32(char transa, char transb, char offsetc, dim_t M, dim_t N, dim_t K, float alpha, const int8_t *A, dim_t lda, int8_t ao, const int8_t *B, dim_t ldb, int8_t bo, float beta, int32_t *C, dim_t ldc, const int32_t *co, void *th) { threadpool_utils::activate_threadpool( (dnnl::threadpool_interop::threadpool_iface *)th); status_t status = dnnl_success; MAYBE_VERBOSE(status, "s8", "s8", "s32", MAYBE_RUN_STACK_CHECKER(dnnl_threadpool_interop_gemm_s8s8s32, cpu::gemm_s8s8s32, &transb, &transa, c2f_offsetC(&offsetc), &N, &M, &K, &alpha, B, &ldb, &bo, A, &lda, &ao, &beta, C, &ldc, co)); threadpool_utils::deactivate_threadpool(); return status; } extern "C" dnnl_status_t DNNL_API dnnl_threadpool_interop_gemm_bf16bf16f32( char transa, char transb, dim_t M, dim_t N, dim_t K, float alpha, const bfloat16_t *A, dim_t lda, const bfloat16_t *B, dim_t ldb, float beta, float *C, dim_t ldc, void *th) { threadpool_utils::activate_threadpool( (dnnl::threadpool_interop::threadpool_iface *)th); status_t status = dnnl_success; MAYBE_VERBOSE(status, "bf16", "bf16", "f32", MAYBE_RUN_STACK_CHECKER(dnnl_threadpool_interop_gemm_bf16bf16f32, cpu::gemm_bf16bf16f32, &transb, &transa, &N, &M, &K, &alpha, B, &ldb, A, &lda, &beta, C, &ldc)); threadpool_utils::deactivate_threadpool(); return status; } #undef MAYBE_VERBOSE #endif