#ifndef GRAPH_BACKEND_DNNL_KERNELS_SDP_DECOMP_CONFIG_HPP
#define GRAPH_BACKEND_DNNL_KERNELS_SDP_DECOMP_CONFIG_HPP
#include <algorithm>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "oneapi/dnnl/dnnl.hpp"
#include "common/dnnl_thread.hpp"
#include "graph/interface/c_types_map.hpp"
#include "graph/backend/dnnl/scratchpad.hpp"
#include "graph/backend/dnnl/subgraph.hpp"
namespace dnnl {
namespace impl {
namespace graph {
namespace dnnl_impl {
using ltw = logical_tensor_wrapper_t;
using op_ptr = std::shared_ptr<op_t>;
using registry_key = size_t;
struct sdp_reorder_t {
public:
status_t init(const dnnl::reorder::primitive_desc &pd) {
auto src_desc = pd.src_desc();
auto dst_desc = pd.dst_desc();
if (src_desc == dst_desc) is_inplace_ = true;
reorder_prim_ = reorder(pd);
return status::success;
}
bool get_inplace() const { return is_inplace_; }
status_t execute(const dnnl::stream &astream,
const std::unordered_map<int, dnnl::memory> &args) const {
if (is_inplace_) {
void *handle = args.at(DNNL_ARG_SRC).get_data_handle();
args.at(DNNL_ARG_DST).set_data_handle(handle);
} else
dnnl_primitive_execute_without_tp_hook(
reorder_prim_, astream, args);
return status::success;
}
private:
dnnl::primitive reorder_prim_;
bool is_inplace_ = false;
};
struct sdp_decomp_config_t {
public:
sdp_decomp_config_t() = default;
dim_t ndims, batch_size, num_head_q, num_head_kv, seq_len_q, seq_len_kv;
dim_t head_size_qk, head_size_v;
dims src1_strides, wei1_strides, wei2_strides, dst_strides,
post_add_strides;
int nthr;
std::vector<int> graph_inport;
enum input_index_t {
mm1_src = 0,
mm1_wei,
mm2_wei,
mm1_scale,
mm1_soft_capping,
mm1_add,
select_condition,
select_other_input
};
primitive sub_mm1_prim, sub_softmax_prim, sub_mm2_prim, sub_select_prim;
sdp_reorder_t sub_reorder0, sub_reorder1, sub_reorder2, sub_reorder3;
std::unordered_map<int, memory> sub_reorder0_args, sub_reorder1_args,
sub_mm1_args, sub_softmax_args, sub_reorder2_args, sub_mm2_args,
sub_reorder3_args, sub_select_args;
std::unordered_map<dnnl_memory_t, registry_key> mem_key_map;
memory sub_src1;
memory sub_wei1_user, sub_wei1_zp;
memory sub_mm1_src, sub_mm1_wei, sub_mm1_dst;
std::vector<memory> sub_mm1_post_mem, sub_softmax_post_mem;
memory sub_select_cond, sub_select_src, sub_select_dst;
memory sub_softmax_dst;
memory sub_wei2_user, sub_wei2_zp;
memory sub_mm2_wei, sub_mm2_dst;
memory sub_dst_user;
memory sub_scratchpad;
memory sub_max_src1_src2, sub_max_dst1_wei2;
bool has_scale = false, has_attention_mask = false, has_select = false,
has_soft_capping = false;
bool select_fusiable = false;
private:
std::vector<op_ptr> sdp_op;
public:
bool initial_check(const std::shared_ptr<subgraph_t> &sg,
const std::vector<logical_tensor_t> &inputs,
const std::vector<logical_tensor_t> &outputs);
template <bool quantized = false,
memory::data_type dt = memory::data_type::f32>
impl::status_t construct_params(std::shared_ptr<subgraph_t> &sg,
registry_t &sdp_registry, const dnnl::engine &p_engine,
const std::vector<logical_tensor_t> &inputs);
private:
op_ptr get_post_op(const op_ptr &op) const;
impl::status_t record_input_offset(const std::shared_ptr<subgraph_t> &sg,
const std::vector<logical_tensor_t> &inputs);
impl::status_t record_sdp_ops(
std::shared_ptr<subgraph_t> &sg, bool is_quantize);
void memory_planning(registry_t &sdp_registry);
impl::status_t prepare_sdp_scales_zps(std::shared_ptr<op_t> &op, int index,
std::unordered_map<int, memory> &args,
const dnnl::engine &p_engine);
template <typename attr_dt, typename target_dt>
target_dt get_attr_value(
std::shared_ptr<op_t> &op, int i, op_attr_t attr_name) {
const auto in_val = op->get_input_value(i);
auto &producer = in_val->get_producer();
return static_cast<target_dt>(
producer.get_attr<std::vector<attr_dt>>(attr_name)[0]);
}
dnnl::primitive_attr make_primitive_attr(std::shared_ptr<op_t> &op);
};
} } } }
#endif