[GPU] SDPA indirect inputs (#24665)
### Details: - Added indirect inputs support for SDPA kernel - Added setter for causal flag for ScaledDotProductAttention operation - Added `ov::intel_gpu::hint::enable_sdpa_optimization` to `ov::supported_properties` list - Removed unused `TARGET_SEQ_LEN_BLOCK_SIZE > 1` check from kernel for single token processing - Minor refactoring - Added `OV_GPU_EnableSDPA` debug option (which allows to force SDPA kernel for any ScaledDotProductAttention operation _(=1)_ / or completely disable SDPA kernel _(=0)_, ignoring `ov::intel_gpu::hint::enable_sdpa_optimization` property) ### Tickets: - *CVS-141213*
This commit is contained in:
parent
af88a206eb
commit
dc523beb3e
|
|
@ -50,6 +50,10 @@ public:
|
|||
return m_causal;
|
||||
}
|
||||
|
||||
void set_causal(bool causal) {
|
||||
m_causal = causal;
|
||||
}
|
||||
|
||||
private:
|
||||
bool m_causal = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "intel_gpu/op/sdpa.hpp"
|
||||
#include "openvino/core/node.hpp"
|
||||
#include "openvino/core/partial_shape.hpp"
|
||||
#include "openvino/op/op.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_gpu {
|
||||
namespace op {
|
||||
|
||||
class IndirectSDPA : public ov::intel_gpu::op::SDPA {
|
||||
public:
|
||||
OPENVINO_OP("IndirectSDPA", "gpu_opset");
|
||||
|
||||
IndirectSDPA() = default;
|
||||
|
||||
IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type = ov::element::undefined);
|
||||
|
||||
IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& attn_mask,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type = ov::element::undefined);
|
||||
|
||||
IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& attn_mask,
|
||||
const ov::Output<Node>& scale,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type = ov::element::undefined);
|
||||
|
||||
bool visit_attributes(ov::AttributeVisitor &visitor) override;
|
||||
void validate_and_infer_types() override;
|
||||
|
||||
std::shared_ptr<Node> clone_with_new_inputs(const ov::OutputVector& new_args) const override;
|
||||
|
||||
ov::element::Type get_output_type() const { return m_output_type; }
|
||||
|
||||
int64_t get_indirect_axis() const { return m_indirect_axis; }
|
||||
|
||||
using ov::intel_gpu::op::SDPA::default_order;
|
||||
|
||||
protected:
|
||||
int64_t m_indirect_axis = -1;
|
||||
};
|
||||
|
||||
} // namespace op
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
@ -285,3 +285,4 @@ REGISTER_FACTORY(internal, IndirectGemm);
|
|||
REGISTER_FACTORY(internal, Convolution);
|
||||
REGISTER_FACTORY(internal, Placeholder);
|
||||
REGISTER_FACTORY(internal, SDPA);
|
||||
REGISTER_FACTORY(internal, IndirectSDPA);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
scaled_dot_product_attention(const primitive_id& id,
|
||||
const std::vector<cldnn::input_info> inputs,
|
||||
bool is_causal,
|
||||
int64_t indirect_axis = -1,
|
||||
const std::vector<int64_t>& input_q_transpose_order = {},
|
||||
const std::vector<int64_t>& input_k_transpose_order = {},
|
||||
const std::vector<int64_t>& input_v_transpose_order = {},
|
||||
|
|
@ -26,17 +27,23 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
const padding& output_padding = padding())
|
||||
: primitive_base(id, inputs, {output_padding})
|
||||
, is_causal(is_causal)
|
||||
, has_attn_mask_input(inputs.size() > 3)
|
||||
, has_scale_input(inputs.size() > 4)
|
||||
, indirect_axis(indirect_axis)
|
||||
, input_q_transpose_order(input_q_transpose_order)
|
||||
, input_k_transpose_order(input_k_transpose_order)
|
||||
, input_v_transpose_order(input_v_transpose_order)
|
||||
, output_transpose_order(output_transpose_order) {}
|
||||
, output_transpose_order(output_transpose_order) {
|
||||
auto data_inputs_num = inputs.size();
|
||||
if (indirect_axis != -1)
|
||||
data_inputs_num--;
|
||||
|
||||
has_attn_mask_input = data_inputs_num > 3;
|
||||
has_scale_input = data_inputs_num > 4;
|
||||
}
|
||||
|
||||
bool is_causal = false;
|
||||
bool has_attn_mask_input = false;
|
||||
bool has_scale_input = false;
|
||||
int64_t indirect_axis = -1;
|
||||
|
||||
std::vector<int64_t> input_q_transpose_order;
|
||||
std::vector<int64_t> input_k_transpose_order;
|
||||
|
|
@ -48,6 +55,7 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
seed = hash_combine(seed, is_causal);
|
||||
seed = hash_combine(seed, has_attn_mask_input);
|
||||
seed = hash_combine(seed, has_scale_input);
|
||||
seed = hash_combine(seed, indirect_axis);
|
||||
seed = hash_range(seed, input_q_transpose_order.begin(), input_q_transpose_order.end());
|
||||
seed = hash_range(seed, input_k_transpose_order.begin(), input_k_transpose_order.end());
|
||||
seed = hash_range(seed, input_v_transpose_order.begin(), input_v_transpose_order.end());
|
||||
|
|
@ -64,6 +72,7 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
return is_causal == rhs_casted.is_causal &&
|
||||
has_attn_mask_input == rhs_casted.has_attn_mask_input &&
|
||||
has_scale_input == rhs_casted.has_scale_input &&
|
||||
indirect_axis == rhs_casted.indirect_axis &&
|
||||
input_q_transpose_order == rhs_casted.input_q_transpose_order &&
|
||||
input_k_transpose_order == rhs_casted.input_k_transpose_order &&
|
||||
input_v_transpose_order == rhs_casted.input_v_transpose_order &&
|
||||
|
|
@ -75,6 +84,7 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
ob << is_causal;
|
||||
ob << has_attn_mask_input;
|
||||
ob << has_scale_input;
|
||||
ob << indirect_axis;
|
||||
ob << input_q_transpose_order;
|
||||
ob << input_k_transpose_order;
|
||||
ob << input_v_transpose_order;
|
||||
|
|
@ -86,6 +96,7 @@ struct scaled_dot_product_attention : public primitive_base<scaled_dot_product_a
|
|||
ib >> is_causal;
|
||||
ib >> has_attn_mask_input;
|
||||
ib >> has_scale_input;
|
||||
ib >> indirect_axis;
|
||||
ib >> input_q_transpose_order;
|
||||
ib >> input_k_transpose_order;
|
||||
ib >> input_v_transpose_order;
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ public:
|
|||
std::vector<std::string> forced_impl_types; // Force implementation type either ocl or onednn
|
||||
int max_kernels_per_batch; // Maximum number of kernels in a batch during compiling kernels
|
||||
int impls_cache_capacity; // The maximum number of entries in the kernel impl cache
|
||||
int enable_sdpa; // Allows to control SDPA decomposition
|
||||
int disable_async_compilation; // Disable async compilation
|
||||
int disable_winograd_conv; // Disable Winograd conv
|
||||
int disable_dynamic_impl; // Disable dynamic implementation
|
||||
|
|
|
|||
|
|
@ -2,34 +2,188 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "primitive_base.hpp"
|
||||
#include "multi_stage_primitive.hpp"
|
||||
#include "scaled_dot_product_attention_inst.h"
|
||||
#include "kv_cache_inst.h"
|
||||
|
||||
#include "sdpa/sdpa_kernel_selector.h"
|
||||
#include "sdpa/sdpa_kernel_base.h"
|
||||
|
||||
namespace cldnn {
|
||||
namespace ocl {
|
||||
struct scaled_dot_product_attention_impl : typed_primitive_impl_ocl<scaled_dot_product_attention> {
|
||||
using parent = typed_primitive_impl_ocl<scaled_dot_product_attention>;
|
||||
|
||||
// SDPA impl may create 2 versions of the kernel internally
|
||||
// 1. Default SDPA kernels
|
||||
// 2. SDPA kernels with indirect access to one of the inputs
|
||||
// This feature is used to avoid perf drop when we create single kernel which checks batch size in runtime
|
||||
// Can be reverted once performance of the kernel is improved
|
||||
struct scaled_dot_product_attention_impl : multi_stage_primitive<scaled_dot_product_attention> {
|
||||
using parent = multi_stage_primitive<scaled_dot_product_attention>;
|
||||
using parent::parent;
|
||||
using kernel_selector_t = kernel_selector::sdpa_kernel_selector;
|
||||
using kernel_params_t = kernel_selector::sdpa_params;
|
||||
|
||||
DECLARE_OBJECT_TYPE_SERIALIZATION(cldnn::ocl::scaled_dot_product_attention_impl)
|
||||
|
||||
const uint32_t default_sdpa = 0;
|
||||
const uint32_t indirect_sdpa = 1;
|
||||
|
||||
std::unique_ptr<primitive_impl> clone() const override {
|
||||
return make_unique<scaled_dot_product_attention_impl>(*this);
|
||||
}
|
||||
|
||||
scaled_dot_product_attention_impl() = default;
|
||||
|
||||
scaled_dot_product_attention_impl(const std::vector<kernel_selector::kernel_data>& kd) : parent(kd) {
|
||||
this->can_reuse_memory = true;
|
||||
}
|
||||
|
||||
void load(BinaryInputBuffer& ib) override {
|
||||
parent::load(ib);
|
||||
if (is_dynamic()) {
|
||||
auto& kernel_selector = kernel_selector_t::Instance();
|
||||
auto kernel_impl = kernel_selector.GetImplementation(_kernel_data.kernelName);
|
||||
kernel_impl->GetUpdateDispatchDataFunc(_kernel_data);
|
||||
auto kernel_impl = kernel_selector.GetImplementation(_kernels_data[default_sdpa].kernelName);
|
||||
kernel_impl->GetUpdateDispatchDataFunc(_kernels_data[default_sdpa]);
|
||||
if (_kernels_data.size() == 2) {
|
||||
auto bt_kernel_impl = kernel_selector.GetImplementation(_kernels_data[indirect_sdpa].kernelName);
|
||||
bt_kernel_impl->GetUpdateDispatchDataFunc(_kernels_data[indirect_sdpa]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<layout> get_internal_buffer_layouts_impl() const override {
|
||||
// TODO: current implementation is supposed to have the same kernel version for both indirect/default paths,
|
||||
// considering this, we may assume that both indirect/default kernels have absolutely the same intermediate
|
||||
// buffers number and its' sizes (since update_dispatch_data is called for both kernels too), and
|
||||
// do not double memory allocations during reallocate_if_needed() function call
|
||||
std::vector<layout> layouts;
|
||||
if (_kernels_data.size() > 0) {
|
||||
auto dtype = from_data_type(_kernels_data[0].internalBufferDataType);
|
||||
const auto bpp = data_type_traits::size_of(dtype);
|
||||
for (auto size : _kernels_data[0].internalBufferSizes) {
|
||||
layout inbuf_layout = {dtype, format::bfyx, // simple linear format (flattern to x channel)
|
||||
{1, 1, 1, (tensor::value_type)(size / bpp)}};
|
||||
layouts.push_back(inbuf_layout);
|
||||
}
|
||||
}
|
||||
|
||||
return layouts;
|
||||
}
|
||||
|
||||
static size_t get_beam_table_id(std::shared_ptr<const scaled_dot_product_attention> primitive) {
|
||||
GPU_DEBUG_TRACE << "get_beam_table_id " << primitive->input_size() - 1 << "\n";
|
||||
return primitive->input_size() - 1;
|
||||
}
|
||||
|
||||
static bool has_indirect_inputs(const kernel_impl_params& impl_param) {
|
||||
const auto& desc = impl_param.typed_desc<scaled_dot_product_attention>();
|
||||
return desc->indirect_axis != -1;
|
||||
}
|
||||
|
||||
kernel_arguments_data get_arguments(const scaled_dot_product_attention_inst& instance, size_t stage) const override {
|
||||
kernel_arguments_data args;
|
||||
|
||||
auto inputs_num = instance.inputs_memory_count();
|
||||
if (instance.has_indirect_inputs() && stage == default_sdpa)
|
||||
inputs_num--;
|
||||
|
||||
for (size_t i = 0; i < inputs_num; i++) {
|
||||
args.inputs.push_back(instance.input_memory_ptr(i));
|
||||
}
|
||||
|
||||
if (instance.has_fused_primitives()) {
|
||||
size_t count = instance.get_fused_mem_count();
|
||||
for (size_t i = 0; i < count; i++) {
|
||||
args.fused_op_inputs.push_back(instance.fused_memory(i));
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < instance.outputs_memory_count(); i++) {
|
||||
args.outputs.push_back(instance.output_memory_ptr(i));
|
||||
}
|
||||
|
||||
args.shape_info = instance.shape_info_memory_ptr();
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
void set_arguments_impl(scaled_dot_product_attention_inst& instance) override {}
|
||||
|
||||
event::ptr execute_stage(const std::vector<event::ptr>& events, scaled_dot_product_attention_inst& instance, size_t stage) {
|
||||
stream& stream = instance.get_network().get_stream();
|
||||
std::vector<event::ptr> tmp_events(events);
|
||||
std::vector<event::ptr> all_events;
|
||||
size_t kernel_offset = 0;
|
||||
|
||||
for (size_t s = 0; s < stage; s++) {
|
||||
kernel_offset += _kernels_data[s].kernels.size();
|
||||
}
|
||||
for (size_t kd_idx = 0; kd_idx < _kernels_data[stage].kernels.size(); ++kd_idx) {
|
||||
if (_kernels_data[stage].kernels[kd_idx].skip_execution)
|
||||
continue;
|
||||
|
||||
size_t idx_final = kernel_offset + kd_idx;
|
||||
// If any user of the desc's users is CPU implementation or network's output, set desc as a output event (event won't be nullptr)
|
||||
bool needs_completion_event = instance.needs_completion_event();
|
||||
|
||||
auto& params = _kernels_data[stage].kernels[kd_idx].params;
|
||||
auto args = get_arguments(instance, stage);
|
||||
args.scalars = ¶ms.scalars;
|
||||
|
||||
for (size_t i = 0; i < instance.get_intermediates_memories().size(); i++)
|
||||
args.intermediates.push_back(instance.get_intermediates_memories()[i]);
|
||||
|
||||
stream.set_arguments(*_kernels[idx_final], _kernels_data[stage].kernels[kd_idx].params, args);
|
||||
|
||||
const auto& gws = params.workGroups.global;
|
||||
const auto& lws = params.workGroups.local;
|
||||
|
||||
GPU_DEBUG_TRACE_DETAIL << "Enqueue stage " << stage << " kernel " << idx_final << ": gws=[" << gws[0] << ", " << gws[1] << ", " << gws[2] << "] "
|
||||
<< "lws=[" << lws[0] << ", " << lws[1] << ", " << lws[2] << "]"
|
||||
<< (needs_completion_event ? " has_completion_event=true" : "") << std::endl;
|
||||
|
||||
auto ev = stream.enqueue_kernel(*_kernels[idx_final], params, args, tmp_events, needs_completion_event);
|
||||
if (_kernels_data[stage].needs_sub_kernels_sync) {
|
||||
tmp_events = {ev};
|
||||
}
|
||||
all_events.push_back(ev);
|
||||
}
|
||||
|
||||
return aggregate_events(all_events, stream, all_events.size() > 1);
|
||||
}
|
||||
|
||||
bool need_indirect_load(const scaled_dot_product_attention_inst& instance) const {
|
||||
auto desc = instance.get_typed_desc<scaled_dot_product_attention>();
|
||||
|
||||
if (!instance.has_indirect_inputs())
|
||||
return false;
|
||||
|
||||
const auto& params = *instance.get_impl_params();
|
||||
const auto indirect_axis = desc->indirect_axis;
|
||||
if (params.input_layouts[get_beam_table_id(desc)].get_partial_shape()[indirect_axis].get_length() == 1)
|
||||
return false;
|
||||
|
||||
const auto& deps = instance.dependencies();
|
||||
|
||||
const auto indirect_dep_idx = 1;
|
||||
const auto& indirect_dep = deps[indirect_dep_idx].first;
|
||||
if (dynamic_cast<const kv_cache_inst*>(indirect_dep) == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto state_layout = indirect_dep->get_impl_params()->get_input_layout(0);
|
||||
bool is_prefill = state_layout.count() == 0;
|
||||
return !is_prefill;
|
||||
}
|
||||
|
||||
event::ptr execute_impl(const std::vector<event::ptr>& events, scaled_dot_product_attention_inst& instance) override {
|
||||
if (need_indirect_load(instance))
|
||||
return execute_stage(events, instance, indirect_sdpa);
|
||||
else
|
||||
return execute_stage(events, instance, default_sdpa);
|
||||
}
|
||||
|
||||
static kernel_selector::sdpa_configuration get_sdpa_configuration(const kernel_impl_params& impl_param) {
|
||||
kernel_selector::sdpa_configuration config;
|
||||
|
||||
|
|
@ -44,16 +198,16 @@ struct scaled_dot_product_attention_impl : typed_primitive_impl_ocl<scaled_dot_p
|
|||
return transposed_pshape;
|
||||
};
|
||||
|
||||
const auto& prim = impl_param.typed_desc<scaled_dot_product_attention>();
|
||||
const auto query_shape = transpose_pshape(impl_param.get_input_layout(0).get_partial_shape(), prim->input_q_transpose_order);
|
||||
const auto key_shape = transpose_pshape(impl_param.get_input_layout(1).get_partial_shape(), prim->input_k_transpose_order);
|
||||
const auto value_shape = transpose_pshape(impl_param.get_input_layout(2).get_partial_shape(), prim->input_v_transpose_order);
|
||||
const auto& desc = impl_param.typed_desc<scaled_dot_product_attention>();
|
||||
const auto query_shape = transpose_pshape(impl_param.get_input_layout(0).get_partial_shape(), desc->input_q_transpose_order);
|
||||
const auto key_shape = transpose_pshape(impl_param.get_input_layout(1).get_partial_shape(), desc->input_k_transpose_order);
|
||||
const auto value_shape = transpose_pshape(impl_param.get_input_layout(2).get_partial_shape(), desc->input_v_transpose_order);
|
||||
|
||||
OPENVINO_ASSERT(key_shape == value_shape, "[GPU] The shapes of key and value inputs are expected to be equal");
|
||||
for (size_t i = 0; i < query_shape.size(); ++i) {
|
||||
if (query_shape[i].is_static() && key_shape[i].is_static() && value_shape[i].is_static()) {
|
||||
if (query_shape[i].get_length() > key_shape[i].get_length()) {
|
||||
config.broadcast_axis = prim->input_k_transpose_order[i];
|
||||
config.broadcast_axis = desc->input_k_transpose_order[i];
|
||||
config.group_size = query_shape[i].get_length() / key_shape[i].get_length();
|
||||
}
|
||||
}
|
||||
|
|
@ -62,44 +216,73 @@ struct scaled_dot_product_attention_impl : typed_primitive_impl_ocl<scaled_dot_p
|
|||
if (query_shape[query_shape.size() - 1].is_static())
|
||||
config.head_size = query_shape[query_shape.size() - 1].get_length();
|
||||
|
||||
config.is_causal = prim->is_causal;
|
||||
config.is_causal = desc->is_causal;
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
static kernel_params_t get_kernel_params(const kernel_impl_params& impl_param, bool is_dynamic) {
|
||||
public:
|
||||
static kernel_params_t get_kernel_params(const kernel_impl_params& impl_param, bool is_dynamic, bool indirect = false) {
|
||||
const auto& desc = impl_param.typed_desc<scaled_dot_product_attention>();
|
||||
auto params = get_default_params<kernel_selector::sdpa_params>(impl_param, is_dynamic);
|
||||
|
||||
const auto inputs_num = impl_param.input_layouts.size();
|
||||
params.inputs.resize(inputs_num);
|
||||
for (size_t i = 0; i < inputs_num; i++) {
|
||||
auto data_inputs_num = impl_param.input_layouts.size();
|
||||
if (has_indirect_inputs(impl_param))
|
||||
data_inputs_num--;
|
||||
|
||||
params.inputs.resize(data_inputs_num);
|
||||
for (size_t i = 0; i < data_inputs_num; i++) {
|
||||
params.inputs[i] = convert_data_tensor(impl_param.get_input_layout(i));
|
||||
}
|
||||
|
||||
params.conf = get_sdpa_configuration(impl_param);
|
||||
|
||||
const auto& prim = impl_param.typed_desc<scaled_dot_product_attention>();
|
||||
params.input0_order = prim->input_q_transpose_order;
|
||||
params.input1_order = prim->input_k_transpose_order;
|
||||
params.input2_order = prim->input_v_transpose_order;
|
||||
params.output_order = prim->output_transpose_order;
|
||||
params.input0_order = desc->input_q_transpose_order;
|
||||
params.input1_order = desc->input_k_transpose_order;
|
||||
params.input2_order = desc->input_v_transpose_order;
|
||||
params.output_order = desc->output_transpose_order;
|
||||
|
||||
if (indirect && has_indirect_inputs(impl_param)) {
|
||||
params.beam_table = convert_data_tensor(impl_param.get_input_layout(get_beam_table_id(desc)));
|
||||
params.indirect_axis = desc->indirect_axis;
|
||||
}
|
||||
|
||||
params.set_dynamic_shape_offsets();
|
||||
|
||||
// Need to adjust sdpa kernel offset to consider beam table input
|
||||
if (has_indirect_inputs(impl_param)) {
|
||||
auto out_offset = params.outputs[0].get_dynamic_shape_offset();
|
||||
if (indirect)
|
||||
params.beam_table.SetDynamicShapeOffset(out_offset);
|
||||
|
||||
params.outputs[0].SetDynamicShapeOffset(out_offset + kernel_selector::DataTensor::max_rank());
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
static std::unique_ptr<primitive_impl> create(const typed_program_node<scaled_dot_product_attention>& arg, const kernel_impl_params& impl_param) {
|
||||
std::vector<kernel_selector::kernel_data> kernels_data;
|
||||
auto sdpa_kernel_params = get_kernel_params(impl_param, impl_param.is_dynamic());
|
||||
auto& sdpa_kernel_selector = kernel_selector_t::Instance();
|
||||
auto kd = sdpa_kernel_selector.get_best_kernel(sdpa_kernel_params);
|
||||
auto& kernel_selector = kernel_selector_t::Instance();
|
||||
kernels_data.push_back(kernel_selector.get_best_kernel(sdpa_kernel_params));
|
||||
|
||||
return cldnn::make_unique<scaled_dot_product_attention_impl>(kd);
|
||||
if (has_indirect_inputs(impl_param)) {
|
||||
auto indirect_kernel_params = get_kernel_params(impl_param, impl_param.is_dynamic(), true);
|
||||
kernels_data.push_back(kernel_selector.get_best_kernel(indirect_kernel_params));
|
||||
}
|
||||
|
||||
return cldnn::make_unique<scaled_dot_product_attention_impl>(kernels_data);
|
||||
}
|
||||
|
||||
void update_dispatch_data(const kernel_impl_params& impl_param) override {
|
||||
auto kernel_params = get_kernel_params(impl_param, true);
|
||||
(_kernel_data.update_dispatch_data_func)(kernel_params, _kernel_data);
|
||||
(_kernels_data[default_sdpa].update_dispatch_data_func)(kernel_params, _kernels_data[default_sdpa]);
|
||||
|
||||
if (_kernels_data.size() == 2) {
|
||||
auto kernel_params = get_kernel_params(impl_param, true);
|
||||
(_kernels_data[indirect_sdpa].update_dispatch_data_func)(kernel_params, _kernels_data[indirect_sdpa]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ public:
|
|||
static std::vector<layout> calc_output_layouts(scaled_dot_product_attention_node const& /*node*/, const kernel_impl_params& impl_param);
|
||||
static layout calc_output_layout(scaled_dot_product_attention_node const& node, kernel_impl_params const& impl_param);
|
||||
static std::string to_string(scaled_dot_product_attention_node const& node);
|
||||
bool has_indirect_inputs() const {
|
||||
return get_typed_desc<scaled_dot_product_attention>()->indirect_axis != -1;
|
||||
}
|
||||
|
||||
typed_primitive_inst(network& network, scaled_dot_product_attention_node const& desc);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -96,6 +96,24 @@ inline uint FUNC(get_input2_index)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
inline uint FUNC(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
#if BEAM_TABLE_SIMPLE
|
||||
return GET_DATA_INDEX_6D_SAFE(BEAM_TABLE, b, f, w, z, y, x);
|
||||
#else
|
||||
# error sdpa_ref.cl : Unsupported beam table format
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint FUNC(get_bt_index_key)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
return FUNC_CALL(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_TENSOR INPUT1_DIMS_ORDER);
|
||||
}
|
||||
|
||||
inline uint FUNC(get_bt_index_value)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
return FUNC_CALL(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_TENSOR INPUT2_DIMS_ORDER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define VALUE_BLOCK_READ(ptr, offset) BLOCK_READN(INPUT2_TYPE, 1, ptr, offset)
|
||||
#define SUBGROUPS_PER_WG (HEAD_SIZE / SUBGROUP_SIZE)
|
||||
|
||||
|
|
@ -117,6 +135,9 @@ KERNEL(sdpa_opt)(
|
|||
const __global INPUT4_TYPE* scale,
|
||||
#endif
|
||||
__global OUTPUT_TYPE* output,
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const __global BEAM_TABLE_TYPE* beam_table,
|
||||
#endif
|
||||
__global SOFTMAX_ACCUMULATOR_TYPE* exp_sums,
|
||||
__global SOFTMAX_ACCUMULATOR_TYPE* max_logits,
|
||||
__global OUTPUT_TYPE* tmp_out
|
||||
|
|
@ -125,12 +146,7 @@ KERNEL(sdpa_opt)(
|
|||
const uint batch_idx = get_global_id(0);
|
||||
const uint b0_idx = batch_idx / NUM_HEADS; /* BATCH dim */
|
||||
const uint b1_idx = batch_idx % NUM_HEADS; /* HEADS_NUM dim */
|
||||
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint target_seq_idx = (uint)get_global_id(1) * TARGET_SEQ_LEN_BLOCK_SIZE;
|
||||
#else
|
||||
const uint target_seq_idx = get_global_id(1);
|
||||
#endif
|
||||
const uint lid = get_local_id(2);
|
||||
const uint head_size_idx = lid;
|
||||
|
||||
|
|
@ -173,12 +189,7 @@ KERNEL(sdpa_opt)(
|
|||
// Query input loading to SLM
|
||||
#define QUERY_STEP_LOCAL SUBGROUP_SIZE * SUBGROUPS_PER_WG
|
||||
uint query_local_offset = sgid * SUBGROUP_SIZE + sglid;
|
||||
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint seq_idx_end = min(TARGET_SEQ_LEN - target_seq_idx, (uint)TARGET_SEQ_LEN_BLOCK_SIZE);
|
||||
#else
|
||||
const uint seq_idx_end = 1;
|
||||
#endif
|
||||
#ifdef INPUT0_DIMS_ORDER
|
||||
uint query_offset = FUNC_CALL(get_input0_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, target_seq_idx, (sgid * SUBGROUP_SIZE));
|
||||
uint query_offset_next_seq = FUNC_CALL(get_input0_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, target_seq_idx + 1, (sgid * SUBGROUP_SIZE));
|
||||
|
|
@ -207,9 +218,14 @@ KERNEL(sdpa_opt)(
|
|||
// HEAD_SIZE / SUBGROUPS_PER_WG times in the loop and saves the result to the qk_local SLM buffer
|
||||
for (uint seq_len = sgid; seq_len < partition_seq_len; seq_len += (HEAD_SIZE / SUBGROUP_SIZE)) {
|
||||
#ifdef INPUT1_DIMS_ORDER
|
||||
uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len, 0);
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_key)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len, 0)];
|
||||
#else
|
||||
uint key_offset = INPUT1_GET_INDEX(b0_idx, b1_idx, start_partition_idx + seq_len, 0);
|
||||
const uint b_idx = b0_idx;
|
||||
#endif
|
||||
const uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + seq_len, 0);
|
||||
#else
|
||||
const uint key_offset = INPUT1_GET_INDEX(b0_idx, b1_idx, start_partition_idx + seq_len, 0);
|
||||
#endif
|
||||
|
||||
INPUT0_TYPE acc[TARGET_SEQ_LEN_BLOCK_SIZE] = {INPUT0_VAL_ZERO};
|
||||
|
|
@ -316,11 +332,7 @@ KERNEL(sdpa_opt)(
|
|||
barrier(CLK_LOCAL_MEM_FENCE);
|
||||
|
||||
INPUT0_TYPE qk_val[TARGET_SEQ_LEN_BLOCK_SIZE];
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint seq_idx_end = min(TARGET_SEQ_LEN - target_seq_idx, (uint)TARGET_SEQ_LEN_BLOCK_SIZE);
|
||||
#else
|
||||
const uint seq_idx_end = 1;
|
||||
#endif
|
||||
for (uint seq_idx = 0; seq_idx < seq_idx_end; seq_idx++) {
|
||||
// Iterate over all values QK values in SLM and apply scale and attention mask
|
||||
for (uint seq_len = sgid * SUBGROUP_SIZE + sglid; seq_len < partition_seq_len; seq_len += (HEAD_SIZE)) {
|
||||
|
|
@ -349,11 +361,7 @@ KERNEL(sdpa_opt)(
|
|||
|
||||
{
|
||||
// SoftMax calculation
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint seq_idx_end = min(TARGET_SEQ_LEN - target_seq_idx, (uint)TARGET_SEQ_LEN_BLOCK_SIZE);
|
||||
#else
|
||||
const uint seq_idx_end = 1;
|
||||
#endif
|
||||
// Find the maximum value of qk in the subgroup
|
||||
for (uint seq_idx = 0; seq_idx < seq_idx_end; seq_idx++) {
|
||||
qk_max[seq_idx] = sub_group_reduce_max(qk_max[seq_idx]);
|
||||
|
|
@ -446,20 +454,26 @@ KERNEL(sdpa_opt)(
|
|||
{
|
||||
// Gemm2 calculation
|
||||
OUTPUT_TYPE acc[TARGET_SEQ_LEN_BLOCK_SIZE] = {OUTPUT_VAL_ZERO};
|
||||
|
||||
#ifndef BEAM_TABLE_TYPE
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, 0, 0);
|
||||
uint value_offset_next_seq = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, 1, 0);
|
||||
const uint value_pitch = value_offset_next_seq - value_offset;
|
||||
#else
|
||||
const uint value_pitch = HEAD_SIZE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (uint seq_len = 0; seq_len < partition_seq_len / SUBGROUP_SIZE; seq_len++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, (head_size_idx / SUBGROUP_SIZE) * SUBGROUP_SIZE)];
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, (head_size_idx / SUBGROUP_SIZE) * SUBGROUP_SIZE);
|
||||
#else
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#else
|
||||
uint value_offset = INPUT2_GET_INDEX(b0_idx, b1_idx, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
OUTPUT_TYPE qk_val[TARGET_SEQ_LEN_BLOCK_SIZE];
|
||||
|
|
@ -468,19 +482,30 @@ KERNEL(sdpa_opt)(
|
|||
}
|
||||
|
||||
unroll_for (uint i = 0; i < SUBGROUP_SIZE; i++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, sub_group_broadcast(value_offset, i));
|
||||
#else
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, value_offset);
|
||||
#endif
|
||||
unroll_for (uint seq_idx = 0; seq_idx < TARGET_SEQ_LEN_BLOCK_SIZE; seq_idx++) {
|
||||
acc[seq_idx] = mad(sub_group_broadcast(qk_val[seq_idx], i), value_val, acc[seq_idx]);
|
||||
}
|
||||
|
||||
#ifndef BEAM_TABLE_TYPE
|
||||
value_offset += value_pitch;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
const uint seq_len_leftovers_start = (partition_seq_len / SUBGROUP_SIZE) * SUBGROUP_SIZE;
|
||||
for (uint seq_len = seq_len_leftovers_start; seq_len < partition_seq_len; seq_len++) {
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
const uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len, head_size_idx);
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len, head_size_idx)];
|
||||
#else
|
||||
const uint b_idx = b0_idx;
|
||||
#endif
|
||||
const uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + seq_len, head_size_idx);
|
||||
#else
|
||||
const uint value_offset = INPUT2_GET_INDEX(b0_idx, b1_idx, start_partition_idx + seq_len, head_size_idx);
|
||||
#endif
|
||||
|
|
@ -500,11 +525,7 @@ KERNEL(sdpa_opt)(
|
|||
// If the number of partitions is greater than 1, save results to the temporary buffer;
|
||||
// otherwise, save results directly to the main output.
|
||||
if (num_of_partitions > 1) {
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint seq_idx_end = min(TARGET_SEQ_LEN - target_seq_idx, (uint)TARGET_SEQ_LEN_BLOCK_SIZE);
|
||||
#else
|
||||
const uint seq_idx_end = 1;
|
||||
#endif
|
||||
for (uint seq_idx = 0; seq_idx < seq_idx_end; seq_idx++) {
|
||||
// Data layout of tmp_output buf: [batch, heads_num, q_len, partition_idx, head_size]
|
||||
const uint tmp_out_offset = b0_idx * (NUM_HEADS * TARGET_SEQ_LEN * num_of_partitions * HEAD_SIZE) +
|
||||
|
|
@ -515,15 +536,11 @@ KERNEL(sdpa_opt)(
|
|||
tmp_out[tmp_out_offset] = acc[seq_idx];
|
||||
}
|
||||
} else {
|
||||
#if TARGET_SEQ_LEN_BLOCK_SIZE > 1
|
||||
const uint seq_idx_end = min(TARGET_SEQ_LEN - target_seq_idx, (uint)TARGET_SEQ_LEN_BLOCK_SIZE);
|
||||
#else
|
||||
const uint seq_idx_end = 1;
|
||||
#endif
|
||||
for (uint seq_idx = 0; seq_idx < seq_idx_end; seq_idx++) {
|
||||
const uint output_offset = OUTPUT_GET_INDEX(b0_idx, b1_idx, target_seq_idx + seq_idx, head_size_idx);
|
||||
const uint output_offset = OUTPUT_GET_INDEX(b0_idx, b1_idx, target_seq_idx + seq_idx, head_size_idx);
|
||||
|
||||
output[output_offset] = acc[seq_idx];
|
||||
output[output_offset] = acc[seq_idx];
|
||||
}
|
||||
}
|
||||
} // Gemm2 calculation end
|
||||
|
|
@ -545,6 +562,9 @@ KERNEL(sdpa_opt)(
|
|||
const __global INPUT4_TYPE* scale,
|
||||
#endif
|
||||
__global OUTPUT_TYPE* output,
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const __global BEAM_TABLE_TYPE* beam_table,
|
||||
#endif
|
||||
__global SOFTMAX_ACCUMULATOR_TYPE* exp_sums,
|
||||
__global SOFTMAX_ACCUMULATOR_TYPE* max_logits,
|
||||
__global OUTPUT_TYPE* tmp_out
|
||||
|
|
@ -637,6 +657,10 @@ KERNEL(sdpa_opt)(
|
|||
// Main Gemm1 calculation loop
|
||||
uint seq_len = sgid * TARGET_SEQ_LEN_BLOCK_SIZE;
|
||||
for (; seq_len < partition_seq_len; seq_len += SUBGROUPS_PER_WG * SUBGROUP_SIZE) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_key)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len + sglid, 0)];
|
||||
const uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + seq_len + sglid, 0);
|
||||
#else
|
||||
#ifdef INPUT1_DIMS_ORDER
|
||||
uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len, 0);
|
||||
uint key_offset_next_seq = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len + 1, 0);
|
||||
|
|
@ -644,6 +668,7 @@ KERNEL(sdpa_opt)(
|
|||
#else
|
||||
uint key_offset = INPUT1_GET_INDEX(b0_idx, b1_idx, start_partition_idx + seq_len, 0);
|
||||
const uint key_pitch = HEAD_SIZE;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
INPUT0_TYPE acc[TARGET_SEQ_LEN_BLOCK_SIZE] = {INPUT0_VAL_ZERO};
|
||||
|
|
@ -660,7 +685,11 @@ KERNEL(sdpa_opt)(
|
|||
}
|
||||
|
||||
unroll_for (uint key_row_idx = 0; key_row_idx < TARGET_SEQ_LEN_BLOCK_SIZE; key_row_idx++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
INPUT1_TYPE key_vals = KEY_BLOCK_READ(key_input, sub_group_broadcast(key_offset, key_row_idx) + head_idx_index);
|
||||
#else
|
||||
INPUT1_TYPE key_vals = KEY_BLOCK_READ(key_input, key_offset + key_row_idx * key_pitch + head_idx_index);
|
||||
#endif
|
||||
|
||||
unroll_for (uint i = 0; i < SUBGROUP_SIZE; i++) {
|
||||
acc[key_row_idx] = mad(sub_group_broadcast(key_vals, i), queries_vec[i], acc[key_row_idx]);
|
||||
|
|
@ -801,10 +830,15 @@ KERNEL(sdpa_opt)(
|
|||
#endif
|
||||
|
||||
for (uint seq_len = 0; seq_len < partition_seq_len / SUBGROUP_SIZE; seq_len++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, sgid * SUBGROUP_SIZE)];
|
||||
const uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, sgid * SUBGROUP_SIZE);
|
||||
#else
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#else
|
||||
uint value_offset = INPUT2_GET_INDEX(b0_idx, b1_idx, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
OUTPUT_TYPE qk_val[TARGET_SEQ_LEN_BLOCK_SIZE];
|
||||
|
|
@ -813,12 +847,18 @@ KERNEL(sdpa_opt)(
|
|||
}
|
||||
|
||||
unroll_for (uint i = 0; i < SUBGROUP_SIZE; i++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, sub_group_broadcast(value_offset, i));
|
||||
#else
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, value_offset);
|
||||
#endif
|
||||
unroll_for (uint seq_idx = 0; seq_idx < TARGET_SEQ_LEN_BLOCK_SIZE; seq_idx++) {
|
||||
acc[seq_idx] = mad(sub_group_broadcast(qk_val[seq_idx], i), value_val, acc[seq_idx]);
|
||||
}
|
||||
|
||||
#ifndef BEAM_TABLE_TYPE
|
||||
value_offset += value_pitch;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -833,17 +873,31 @@ KERNEL(sdpa_opt)(
|
|||
qk_val[seq_idx] = qk_local[qk_offset];
|
||||
qk_offset += SEQ_LEN_PARTITION_SIZE;
|
||||
}
|
||||
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len_leftovers_start + sglid, sgid * SUBGROUP_SIZE)];
|
||||
const uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + seq_len_leftovers_start + sglid, sgid * SUBGROUP_SIZE);
|
||||
#else
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + seq_len_leftovers_start, head_size_idx);
|
||||
#else
|
||||
uint value_offset = INPUT2_GET_INDEX(b0_idx, b1_idx, start_partition_idx + seq_len_leftovers_start, head_size_idx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
for (uint seq_len_idx = 0; seq_len_idx < partition_seq_len - seq_len_leftovers_start; seq_len_idx++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, sub_group_broadcast(value_offset, seq_len_idx));
|
||||
#else
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, value_offset);
|
||||
#endif
|
||||
|
||||
for (uint seq_idx = 0; seq_idx < TARGET_SEQ_LEN_BLOCK_SIZE; seq_idx++) {
|
||||
acc[seq_idx] = mad(sub_group_broadcast(qk_val[seq_idx], seq_len_idx), value_val, acc[seq_idx]);
|
||||
}
|
||||
|
||||
#ifndef BEAM_TABLE_TYPE
|
||||
value_offset += value_pitch;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -890,10 +944,15 @@ KERNEL(sdpa_opt)(
|
|||
#endif
|
||||
|
||||
for (uint seq_len = 0; seq_len < partition_seq_len / SUBGROUP_SIZE; seq_len++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, sgid * SUBGROUP_SIZE)];
|
||||
const uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE) + sglid, sgid * SUBGROUP_SIZE);
|
||||
#else
|
||||
#ifdef INPUT2_DIMS_ORDER
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0_idx, b1_idx, 0, 0, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#else
|
||||
uint value_offset = INPUT2_GET_INDEX(b0_idx, b1_idx, start_partition_idx + (seq_len * SUBGROUP_SIZE), head_size_idx);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
OUTPUT_TYPE qk_val[TARGET_SEQ_LEN_BLOCK_SIZE];
|
||||
|
|
@ -902,12 +961,18 @@ KERNEL(sdpa_opt)(
|
|||
}
|
||||
|
||||
unroll_for (uint i = 0; i < SUBGROUP_SIZE; i++) {
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, sub_group_broadcast(value_offset, i));
|
||||
#else
|
||||
INPUT2_TYPE value_val = VALUE_BLOCK_READ(value_input, value_offset);
|
||||
#endif
|
||||
unroll_for (uint seq_idx = 0; seq_idx < TARGET_SEQ_LEN_BLOCK_SIZE; seq_idx++) {
|
||||
acc[seq_idx] = mad(sub_group_broadcast(qk_val[seq_idx], i), value_val, acc[seq_idx]);
|
||||
}
|
||||
|
||||
#ifndef BEAM_TABLE_TYPE
|
||||
value_offset += value_pitch;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,6 +93,24 @@ inline uint FUNC(get_input2_index)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint
|
|||
#endif
|
||||
}
|
||||
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
inline uint FUNC(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
#if BEAM_TABLE_SIMPLE
|
||||
return GET_DATA_INDEX_6D_SAFE(BEAM_TABLE, b, f, w, z, y, x);
|
||||
#else
|
||||
# error sdpa_ref.cl : Unsupported beam table format
|
||||
#endif
|
||||
}
|
||||
|
||||
inline uint FUNC(get_bt_index_key)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
return FUNC_CALL(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_TENSOR INPUT1_DIMS_ORDER);
|
||||
}
|
||||
|
||||
inline uint FUNC(get_bt_index_value)(OPTIONAL_SHAPE_INFO_ARG uint b, uint f, uint w, uint z, uint y, uint x) {
|
||||
return FUNC_CALL(get_bt_index_nt)(OPTIONAL_SHAPE_INFO_TENSOR INPUT2_DIMS_ORDER);
|
||||
}
|
||||
#endif
|
||||
|
||||
#define APPLY_SCALE_TO_QUERY 1
|
||||
|
||||
KERNEL(sdpa_ref)(
|
||||
|
|
@ -107,6 +125,9 @@ KERNEL(sdpa_ref)(
|
|||
const __global INPUT4_TYPE* scale,
|
||||
#endif
|
||||
__global OUTPUT_TYPE* output,
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
const __global BEAM_TABLE_TYPE* beam_table,
|
||||
#endif
|
||||
__global OUTPUT_TYPE* tmp_buf
|
||||
)
|
||||
{
|
||||
|
|
@ -129,7 +150,12 @@ KERNEL(sdpa_ref)(
|
|||
OUTPUT_TYPE acc = 0;
|
||||
for (uint h = 0; h < HEAD_SIZE /* head_size */; h++) {
|
||||
uint query_offset = FUNC_CALL(get_input0_index)(OPTIONAL_SHAPE_INFO_TENSOR b0, b1, 0, 0, target_seq_idx, h);
|
||||
uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b0, b1, 0, 0, s, h);
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
uint b_idx = beam_table[FUNC_CALL(get_bt_index_key)(OPTIONAL_SHAPE_INFO_TENSOR b0, b1, 0, 0, s, h)];
|
||||
#else
|
||||
uint b_idx = b0;
|
||||
#endif
|
||||
uint key_offset = FUNC_CALL(get_input1_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1, 0, 0, s, h);
|
||||
|
||||
#if APPLY_SCALE_TO_QUERY
|
||||
INPUT0_TYPE q_val = query_input[query_offset] * scale_val;
|
||||
|
|
@ -202,7 +228,13 @@ KERNEL(sdpa_ref)(
|
|||
uint tmp_buf_offset = b0 * (NUM_HEADS * TARGET_SEQ_LEN * SOURCE_SEQ_LEN) +
|
||||
b1 * (TARGET_SEQ_LEN * SOURCE_SEQ_LEN) +
|
||||
target_seq_idx * (SOURCE_SEQ_LEN) + s;
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b0, b1, 0, 0, s, head_size_idx);
|
||||
|
||||
#ifdef BEAM_TABLE_TYPE
|
||||
uint b_idx = beam_table[FUNC_CALL(get_bt_index_value)(OPTIONAL_SHAPE_INFO_TENSOR b0, b1, 0, 0, s, head_size_idx)];
|
||||
#else
|
||||
uint b_idx = b0;
|
||||
#endif
|
||||
uint value_offset = FUNC_CALL(get_input2_index)(OPTIONAL_SHAPE_INFO_TENSOR b_idx, b1, 0, 0, s, head_size_idx);
|
||||
|
||||
acc += tmp_buf[tmp_buf_offset] * value_input[value_offset];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,15 +85,30 @@ JitConstants SDPAKernelBase::GetJitConstants(const sdpa_params& params) const {
|
|||
return true;
|
||||
};
|
||||
|
||||
if ((!params.input0_order.empty() && !is_default_order(params.input0_order)) || params.conf.broadcast_axis != -1) {
|
||||
auto use_index_calc_func = [&](const std::vector<int64_t> order, bool is_query = false) {
|
||||
if (!params.input0_order.empty() && !is_default_order(params.input0_order))
|
||||
return true;
|
||||
|
||||
if (params.conf.broadcast_axis != -1)
|
||||
return true;
|
||||
|
||||
if (params.indirect_axis != -1 && !is_query)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
if (params.indirect_axis != -1)
|
||||
jit.AddConstant(MakeJitConstant("BEAM_TABLE", params.beam_table));
|
||||
|
||||
if (use_index_calc_func(params.input0_order, true))
|
||||
jit.AddConstant(MakeJitConstant("INPUT0_DIMS_ORDER", GetDimsOrder(params.input0_order)));
|
||||
}
|
||||
if ((!params.input1_order.empty() && !is_default_order(params.input1_order)) || params.conf.broadcast_axis != -1) {
|
||||
|
||||
if (use_index_calc_func(params.input1_order))
|
||||
jit.AddConstant(MakeJitConstant("INPUT1_DIMS_ORDER", GetDimsOrder(params.input1_order)));
|
||||
}
|
||||
if ((!params.input2_order.empty() && !is_default_order(params.input2_order)) || params.conf.broadcast_axis != -1) {
|
||||
|
||||
if (use_index_calc_func(params.input2_order))
|
||||
jit.AddConstant(MakeJitConstant("INPUT2_DIMS_ORDER", GetDimsOrder(params.input2_order)));
|
||||
}
|
||||
|
||||
TransposedDimensionAccessHelperJit dims_q(params.inputs[0], params.input0_order);
|
||||
jit.AddConstant(MakeJitConstant("TARGET_SEQ_LEN", dims_q.y()));
|
||||
|
|
|
|||
|
|
@ -99,6 +99,9 @@ struct sdpa_params : public base_params {
|
|||
std::vector<int64_t> input1_order;
|
||||
std::vector<int64_t> input2_order;
|
||||
std::vector<int64_t> output_order;
|
||||
int64_t indirect_axis = -1;
|
||||
|
||||
DataTensor beam_table;
|
||||
|
||||
sdpa_configuration conf;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -29,6 +29,22 @@ static size_t get_seq_len_partition_size() {
|
|||
return seq_len;
|
||||
}
|
||||
|
||||
static std::string GetKernelName(std::string base_name, KernelsTypes type, bool is_indirect) {
|
||||
auto kernel_name = base_name;
|
||||
if (is_indirect)
|
||||
kernel_name += "_ind";
|
||||
|
||||
if (type == KernelsTypes::SINGLE_TOKEN) {
|
||||
kernel_name += "_single_token";
|
||||
} else if (type == KernelsTypes::MULTI_TOKENS) {
|
||||
kernel_name += "_multi_tokens";
|
||||
} else if (type == KernelsTypes::FINALIZATION) {
|
||||
kernel_name += "_finalization";
|
||||
}
|
||||
|
||||
return kernel_name;
|
||||
}
|
||||
|
||||
ParamsKey SDPAKernelOpt::GetSupportedKey() const {
|
||||
ParamsKey k;
|
||||
k.EnableInputDataType(Datatype::F16);
|
||||
|
|
@ -104,7 +120,7 @@ CommonDispatchData SDPAKernelOpt::SetDefault(const sdpa_params& params, size_t k
|
|||
CeilDiv(target_seq_len, target_seq_len_block_size),
|
||||
head_size * num_of_partitions };
|
||||
dispatch_data.lws = { 1, 1, head_size };
|
||||
} else if (kernel_idx == 2) {
|
||||
} else if (kernel_idx == KernelsTypes::FINALIZATION) {
|
||||
dispatch_data.gws = { batch_size * heads_num,
|
||||
target_seq_len,
|
||||
16 };
|
||||
|
|
@ -134,8 +150,7 @@ KernelsData SDPAKernelOpt::GetKernelsData(const Params& params) const {
|
|||
const auto& prim_params = dynamic_cast<const sdpa_params&>(params);
|
||||
for (size_t kernel_idx = 0; kernel_idx < kernels_num; kernel_idx++) {
|
||||
auto dispatch_data = SetDefault(prim_params, kernel_idx);
|
||||
auto kernel_name = kernel_idx == 0 ? kernelName + "_single_token" :
|
||||
kernel_idx == 1 ? kernelName + "_multi_tokens" : kernelName + "_finalization";
|
||||
auto kernel_name = GetKernelName(kernelName, static_cast<KernelsTypes>(kernel_idx), prim_params.indirect_axis != -1);
|
||||
auto entry_point = GetEntryPoint(kernel_name, prim_params.layerID, params);
|
||||
auto jit_constants = GetJitConstants(prim_params, kernel_idx);
|
||||
auto jit = CreateJit(kernel_name, jit_constants, entry_point);
|
||||
|
|
@ -171,6 +186,9 @@ KernelsData SDPAKernelOpt::GetKernelsData(const Params& params) const {
|
|||
auto tmp_out_elements_count = (num_of_partitions == 1) ? 1 : output.LogicalSize() * num_of_partitions;
|
||||
auto tmp_out_size = tmp_out_elements_count * tmp_out_dt_size;
|
||||
|
||||
if (prim_params.indirect_axis != -1 && kernel_idx != KernelsTypes::FINALIZATION)
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INPUT, static_cast<uint32_t>(prim_params.inputs.size())});
|
||||
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INTERNAL_BUFFER, 0});
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INTERNAL_BUFFER, 1});
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INTERNAL_BUFFER, 2});
|
||||
|
|
|
|||
|
|
@ -13,6 +13,8 @@ ParamsKey SDPAKernelRef::GetSupportedKey() const {
|
|||
ParamsKey k;
|
||||
k.EnableInputDataType(Datatype::F16);
|
||||
k.EnableInputDataType(Datatype::F32);
|
||||
// beam table input
|
||||
k.EnableInputDataType(Datatype::INT32);
|
||||
|
||||
k.EnableOutputDataType(Datatype::F16);
|
||||
k.EnableOutputDataType(Datatype::F32);
|
||||
|
|
@ -72,6 +74,9 @@ KernelsData SDPAKernelRef::GetKernelsData(const Params& params) const {
|
|||
"", false, false, static_cast<int>(prim_params.inputs.size()),
|
||||
GetFusedPrimitiveInputsCount(params), 1, prim_params.is_shape_agnostic);
|
||||
|
||||
if (prim_params.indirect_axis != -1)
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INPUT, static_cast<uint32_t>(prim_params.inputs.size())});
|
||||
|
||||
kernel.params.arguments.push_back({ArgumentDescriptor::Types::INTERNAL_BUFFER, 0});
|
||||
|
||||
kd.internalBufferSizes.clear();
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include "intel_gpu/plugin/common_utils.hpp"
|
||||
|
||||
#include "intel_gpu/op/sdpa.hpp"
|
||||
#include "intel_gpu/op/indirect_sdpa.hpp"
|
||||
|
||||
#include "openvino/op/scaled_dot_product_attention.hpp"
|
||||
|
||||
|
|
@ -15,6 +16,7 @@ namespace ov {
|
|||
namespace op {
|
||||
namespace internal {
|
||||
using SDPA = ov::intel_gpu::op::SDPA;
|
||||
using IndirectSDPA = ov::intel_gpu::op::IndirectSDPA;
|
||||
} // namespace internal
|
||||
} // namespace op
|
||||
} // namespace ov
|
||||
|
|
@ -41,9 +43,30 @@ static void CreateSDPAOp(ProgramBuilder& p, const std::shared_ptr<ov::op::intern
|
|||
auto layerName = layer_type_name_ID(op);
|
||||
|
||||
bool is_causal = op->get_causal();
|
||||
int64_t indirect_axis = -1;
|
||||
auto sdpa_prim = cldnn::scaled_dot_product_attention(layerName,
|
||||
inputs,
|
||||
is_causal,
|
||||
indirect_axis,
|
||||
op->get_input0_transpose_order(),
|
||||
op->get_input1_transpose_order(),
|
||||
op->get_input2_transpose_order(),
|
||||
op->get_output_transpose_order());
|
||||
|
||||
p.add_primitive(*op, sdpa_prim);
|
||||
}
|
||||
|
||||
static void CreateIndirectSDPAOp(ProgramBuilder& p, const std::shared_ptr<ov::op::internal::IndirectSDPA>& op) {
|
||||
validate_inputs_count(op, {4, 5, 6});
|
||||
auto inputs = p.GetInputInfo(op);
|
||||
auto layerName = layer_type_name_ID(op);
|
||||
|
||||
bool is_causal = op->get_causal();
|
||||
int64_t indirect_axis = op->get_indirect_axis();
|
||||
auto sdpa_prim = cldnn::scaled_dot_product_attention(layerName,
|
||||
inputs,
|
||||
is_causal,
|
||||
indirect_axis,
|
||||
op->get_input0_transpose_order(),
|
||||
op->get_input1_transpose_order(),
|
||||
op->get_input2_transpose_order(),
|
||||
|
|
@ -53,6 +76,7 @@ static void CreateSDPAOp(ProgramBuilder& p, const std::shared_ptr<ov::op::intern
|
|||
}
|
||||
|
||||
REGISTER_FACTORY_IMPL(internal, SDPA);
|
||||
REGISTER_FACTORY_IMPL(internal, IndirectSDPA);
|
||||
REGISTER_FACTORY_IMPL(v13, ScaledDotProductAttention);
|
||||
|
||||
} // namespace intel_gpu
|
||||
|
|
|
|||
|
|
@ -542,6 +542,7 @@ std::vector<ov::PropertyName> Plugin::get_supported_properties() const {
|
|||
ov::PropertyName{ov::intel_gpu::hint::host_task_priority.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::intel_gpu::hint::queue_priority.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::intel_gpu::hint::queue_throttle.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::intel_gpu::hint::enable_sdpa_optimization.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::intel_gpu::enable_loop_unrolling.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::intel_gpu::disable_winograd_convolution.name(), PropertyMutability::RW},
|
||||
ov::PropertyName{ov::cache_dir.name(), PropertyMutability::RW},
|
||||
|
|
|
|||
|
|
@ -6,7 +6,9 @@
|
|||
#include <memory>
|
||||
|
||||
#include "intel_gpu/op/gemm.hpp"
|
||||
#include "intel_gpu/op/sdpa.hpp"
|
||||
#include "intel_gpu/op/indirect_gemm.hpp"
|
||||
#include "intel_gpu/op/indirect_sdpa.hpp"
|
||||
#include "intel_gpu/op/kv_cache.hpp"
|
||||
#include "intel_gpu/op/read_value.hpp"
|
||||
#include "intel_gpu/plugin/common_utils.hpp"
|
||||
|
|
@ -42,7 +44,7 @@ void replace_node_unsafe(const std::shared_ptr<ov::Node>& target, const std::sha
|
|||
namespace ov {
|
||||
namespace intel_gpu {
|
||||
|
||||
IndirectKVCache::IndirectKVCache() {
|
||||
IndirectGemmOpt::IndirectGemmOpt() {
|
||||
using namespace ov::pass::pattern;
|
||||
|
||||
auto beam_idx = wrap_type<ov::op::v0::Parameter>();
|
||||
|
|
@ -108,9 +110,141 @@ IndirectKVCache::IndirectKVCache() {
|
|||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(matmul, "IndirectKVCache");
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(matmul, "IndirectGemmOpt");
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
|
||||
IndirectSDPAOpt::IndirectSDPAOpt() {
|
||||
using namespace ov::pass::pattern;
|
||||
using ov::pass::pattern::op::Or;
|
||||
|
||||
auto beam_idx = wrap_type<ov::op::v0::Parameter>();
|
||||
auto gather_input_0 = wrap_type<ov::intel_gpu::op::ReadValue>();
|
||||
auto gather_input_1 = wrap_type<ov::intel_gpu::op::ReadValue>();
|
||||
auto axis_const = wrap_type<ov::op::v0::Constant>(
|
||||
ov::op::util::constant_predicate<int64_t>([](const std::vector<int64_t>& value) -> bool {
|
||||
return value.size() == 1 && (value[0] == 0 || value[0] == 1);
|
||||
}));
|
||||
auto gather_past_0 = wrap_type<ov::op::v8::Gather>({gather_input_0, beam_idx, axis_const});
|
||||
auto gather_past_1 = wrap_type<ov::op::v8::Gather>({gather_input_1, beam_idx, axis_const});
|
||||
auto kv_cache_0 = wrap_type<ov::intel_gpu::op::KVCache>({gather_past_0, any_input()});
|
||||
auto kv_cache_1 = wrap_type<ov::intel_gpu::op::KVCache>({gather_past_1, any_input()});
|
||||
|
||||
auto input_attn_mask = any_input();
|
||||
auto input_scale = any_input();
|
||||
auto sdpa_without_attn_mask_m = wrap_type<ov::intel_gpu::op::SDPA>({ any_input(), kv_cache_0, kv_cache_1 });
|
||||
auto sdpa_with_attn_mask_m = wrap_type<ov::intel_gpu::op::SDPA>({ any_input(), kv_cache_0, kv_cache_1, input_attn_mask });
|
||||
auto sdpa_with_attn_mask_and_scale_m =
|
||||
wrap_type<ov::intel_gpu::op::SDPA>({ any_input(), kv_cache_0, kv_cache_1, input_attn_mask, input_scale });
|
||||
|
||||
auto sdpa_m = std::make_shared<Or>(OutputVector{sdpa_without_attn_mask_m, sdpa_with_attn_mask_m, sdpa_with_attn_mask_and_scale_m});
|
||||
|
||||
ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](ov::pass::pattern::Matcher& m) {
|
||||
if (transformation_callback(m.get_match_root())) {
|
||||
return false;
|
||||
}
|
||||
const auto& pattern_map = m.get_pattern_value_map();
|
||||
|
||||
auto kv_cache_node_0 = std::dynamic_pointer_cast<ov::intel_gpu::op::KVCache>(pattern_map.at(kv_cache_0).get_node_shared_ptr());
|
||||
auto kv_cache_node_1 = std::dynamic_pointer_cast<ov::intel_gpu::op::KVCache>(pattern_map.at(kv_cache_1).get_node_shared_ptr());
|
||||
|
||||
auto beam_idx_node = pattern_map.at(beam_idx).get_node_shared_ptr();
|
||||
auto gather_input_node_0 = pattern_map.at(gather_input_0).get_node_shared_ptr();
|
||||
auto gather_input_node_1 = pattern_map.at(gather_input_1).get_node_shared_ptr();
|
||||
auto gather_node_0 = std::dynamic_pointer_cast<ov::op::v8::Gather>(pattern_map.at(gather_past_0).get_node_shared_ptr());
|
||||
auto gather_node_1 = std::dynamic_pointer_cast<ov::op::v8::Gather>(pattern_map.at(gather_past_1).get_node_shared_ptr());
|
||||
auto gather_axis_0 = gather_node_0->get_axis();
|
||||
auto gather_axis_1 = gather_node_1->get_axis();
|
||||
OPENVINO_ASSERT(gather_axis_0 == gather_axis_1);
|
||||
|
||||
ov::replace_node(gather_node_0, gather_input_node_0);
|
||||
ov::replace_node(gather_node_1, gather_input_node_1);
|
||||
|
||||
auto indirect_kv_cache_0 = std::make_shared<op::KVCache>(gather_input_node_0,
|
||||
kv_cache_node_0->get_input_node_shared_ptr(1),
|
||||
beam_idx_node,
|
||||
kv_cache_node_0->get_variable(),
|
||||
kv_cache_node_0->get_concat_axis(),
|
||||
gather_axis_0,
|
||||
kv_cache_node_0->get_output_element_type(0));
|
||||
|
||||
auto indirect_kv_cache_1 = std::make_shared<op::KVCache>(gather_input_node_1,
|
||||
kv_cache_node_1->get_input_node_shared_ptr(1),
|
||||
beam_idx_node,
|
||||
kv_cache_node_1->get_variable(),
|
||||
kv_cache_node_1->get_concat_axis(),
|
||||
gather_axis_1,
|
||||
kv_cache_node_1->get_output_element_type(0));
|
||||
|
||||
indirect_kv_cache_0->set_friendly_name(kv_cache_node_0->get_friendly_name());
|
||||
indirect_kv_cache_1->set_friendly_name(kv_cache_node_1->get_friendly_name());
|
||||
ov::copy_runtime_info(kv_cache_node_0, indirect_kv_cache_0);
|
||||
ov::copy_runtime_info(kv_cache_node_1, indirect_kv_cache_1);
|
||||
replace_node_unsafe(kv_cache_node_0, indirect_kv_cache_0);
|
||||
replace_node_unsafe(kv_cache_node_1, indirect_kv_cache_1);
|
||||
|
||||
auto sdpa = std::dynamic_pointer_cast<op::SDPA>(m.get_match_root());
|
||||
auto order_in0 = sdpa->get_input0_transpose_order();
|
||||
auto order_in1 = sdpa->get_input1_transpose_order();
|
||||
auto order_in2 = sdpa->get_input2_transpose_order();
|
||||
auto order_out = sdpa->get_output_transpose_order();
|
||||
auto is_causal = sdpa->get_causal();
|
||||
|
||||
std::shared_ptr<op::SDPA> indirect_sdpa;
|
||||
if (pattern_map.find(sdpa_without_attn_mask_m) != pattern_map.end()) {
|
||||
indirect_sdpa = std::make_shared<ov::intel_gpu::op::IndirectSDPA>(sdpa->get_input_node_shared_ptr(0),
|
||||
sdpa->get_input_node_shared_ptr(1),
|
||||
sdpa->get_input_node_shared_ptr(2),
|
||||
indirect_kv_cache_0->output(1), // beam table
|
||||
is_causal,
|
||||
gather_axis_1,
|
||||
order_in0,
|
||||
order_in1,
|
||||
order_in2,
|
||||
order_out);
|
||||
} else if (pattern_map.find(sdpa_with_attn_mask_m) != pattern_map.end()) {
|
||||
indirect_sdpa = std::make_shared<ov::intel_gpu::op::IndirectSDPA>(sdpa->get_input_node_shared_ptr(0),
|
||||
sdpa->get_input_node_shared_ptr(1),
|
||||
sdpa->get_input_node_shared_ptr(2),
|
||||
sdpa->get_input_node_shared_ptr(3),
|
||||
indirect_kv_cache_0->output(1), // beam table
|
||||
is_causal,
|
||||
gather_axis_1,
|
||||
order_in0,
|
||||
order_in1,
|
||||
order_in2,
|
||||
order_out);
|
||||
} else if (pattern_map.find(sdpa_with_attn_mask_and_scale_m) != pattern_map.end()) {
|
||||
indirect_sdpa = std::make_shared<ov::intel_gpu::op::IndirectSDPA>(sdpa->get_input_node_shared_ptr(0),
|
||||
sdpa->get_input_node_shared_ptr(1),
|
||||
sdpa->get_input_node_shared_ptr(2),
|
||||
sdpa->get_input_node_shared_ptr(3),
|
||||
sdpa->get_input_node_shared_ptr(4),
|
||||
indirect_kv_cache_0->output(1), // beam table
|
||||
is_causal,
|
||||
gather_axis_1,
|
||||
order_in0,
|
||||
order_in1,
|
||||
order_in2,
|
||||
order_out);
|
||||
}
|
||||
|
||||
OPENVINO_ASSERT(indirect_sdpa != nullptr);
|
||||
|
||||
indirect_sdpa->set_friendly_name(sdpa->get_friendly_name());
|
||||
ov::copy_runtime_info(sdpa, indirect_sdpa);
|
||||
ov::replace_node(sdpa, indirect_sdpa);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
auto m = std::make_shared<ov::pass::pattern::Matcher>(sdpa_m, "IndirectSDPAOpt");
|
||||
this->register_matcher(m, callback);
|
||||
}
|
||||
|
||||
IndirectKVCache::IndirectKVCache() {
|
||||
add_matcher<IndirectGemmOpt>();
|
||||
add_matcher<IndirectSDPAOpt>();
|
||||
}
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -36,11 +36,22 @@ namespace intel_gpu {
|
|||
/// ┌────┴──────┐ ┌────┴──────┴───┐
|
||||
/// │ Gemm │ | IndirectGemm |
|
||||
/// └───────────┘ └───────────────┘
|
||||
class IndirectKVCache : public ov::pass::MatcherPass {
|
||||
class IndirectKVCache : public ov::pass::GraphRewrite {
|
||||
public:
|
||||
OPENVINO_RTTI("IndirectKVCache", "0");
|
||||
IndirectKVCache();
|
||||
};
|
||||
|
||||
class IndirectGemmOpt : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("IndirectGemmOpt", "0");
|
||||
IndirectGemmOpt();
|
||||
};
|
||||
|
||||
class IndirectSDPAOpt : public ov::pass::MatcherPass {
|
||||
public:
|
||||
OPENVINO_RTTI("IndirectSDPAOpt", "0");
|
||||
IndirectSDPAOpt();
|
||||
};
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
|
|||
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright (C) 2024 Intel Corporation
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
#include "intel_gpu/op/indirect_sdpa.hpp"
|
||||
#include "openvino/core/partial_shape.hpp"
|
||||
|
||||
namespace ov {
|
||||
namespace intel_gpu {
|
||||
namespace op {
|
||||
|
||||
IndirectSDPA::IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type)
|
||||
: ov::intel_gpu::op::SDPA(Q, K, V, order_q, order_k, order_v, order_out, is_causal, output_type)
|
||||
, m_indirect_axis(indirect_axis) {
|
||||
set_argument(3, beam_table);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
IndirectSDPA::IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& attn_mask,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type)
|
||||
: ov::intel_gpu::op::SDPA(Q, K, V, attn_mask, order_q, order_k, order_v, order_out, is_causal, output_type)
|
||||
, m_indirect_axis(indirect_axis) {
|
||||
set_argument(4, beam_table);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
IndirectSDPA::IndirectSDPA(const ov::Output<Node>& Q,
|
||||
const ov::Output<Node>& K,
|
||||
const ov::Output<Node>& V,
|
||||
const ov::Output<Node>& attn_mask,
|
||||
const ov::Output<Node>& scale,
|
||||
const ov::Output<Node>& beam_table,
|
||||
const bool is_causal,
|
||||
const int64_t indirect_axis,
|
||||
const std::vector<int64_t>& order_q,
|
||||
const std::vector<int64_t>& order_k,
|
||||
const std::vector<int64_t>& order_v,
|
||||
const std::vector<int64_t>& order_out,
|
||||
const ov::element::Type output_type)
|
||||
: ov::intel_gpu::op::SDPA(Q, K, V, attn_mask, scale, order_q, order_k, order_v, order_out, is_causal, output_type)
|
||||
, m_indirect_axis(indirect_axis) {
|
||||
set_argument(5, beam_table);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Node> IndirectSDPA::clone_with_new_inputs(const ov::OutputVector& new_args) const {
|
||||
check_new_args_count(this, new_args);
|
||||
|
||||
if (new_args.size() == 4) {
|
||||
return std::make_shared<IndirectSDPA>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3),
|
||||
m_is_causal, m_indirect_axis, m_order_q, m_order_k, m_order_v, m_order_out, m_output_type);
|
||||
} else if (new_args.size() == 5) {
|
||||
return std::make_shared<IndirectSDPA>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3), new_args.at(4),
|
||||
m_is_causal, m_indirect_axis, m_order_q, m_order_k, m_order_v, m_order_out, m_output_type);
|
||||
} else {
|
||||
return std::make_shared<IndirectSDPA>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3), new_args.at(4), new_args.at(5),
|
||||
m_is_causal, m_indirect_axis, m_order_q, m_order_k, m_order_v, m_order_out, m_output_type);
|
||||
}
|
||||
}
|
||||
|
||||
void IndirectSDPA::validate_and_infer_types() {
|
||||
const auto input_size = get_input_size();
|
||||
NODE_VALIDATION_CHECK(this,
|
||||
input_size == 4 || input_size == 5 || input_size == 6,
|
||||
"Number of inputs is incorrect. Current value is: ",
|
||||
input_size,
|
||||
", expected 4, 5 or 6.");
|
||||
|
||||
std::vector<ov::PartialShape> input_shapes;
|
||||
for (size_t i = 0; i < input_size - 1; i++) {
|
||||
input_shapes.push_back(get_input_partial_shape(i));
|
||||
}
|
||||
|
||||
auto out_shapes = shape_infer(this,
|
||||
input_shapes,
|
||||
m_order_q,
|
||||
m_order_k,
|
||||
m_order_v,
|
||||
m_order_out);
|
||||
|
||||
auto output_type = m_output_type == ov::element::undefined ? get_input_element_type(0) : m_output_type;
|
||||
set_output_type(0, output_type, out_shapes[0]);
|
||||
}
|
||||
|
||||
bool IndirectSDPA::visit_attributes(ov::AttributeVisitor &visitor) {
|
||||
SDPA::visit_attributes(visitor);
|
||||
visitor.on_attribute("indirect_axis", m_indirect_axis);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace op
|
||||
} // namespace intel_gpu
|
||||
} // namespace ov
|
||||
|
|
@ -30,6 +30,7 @@ SDPA::SDPA(const ov::Output<Node>& Q,
|
|||
, m_is_causal(is_causal)
|
||||
, m_output_type(output_type) {
|
||||
set_arguments({Q, K, V});
|
||||
set_causal(is_causal);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
|
|
@ -50,6 +51,7 @@ SDPA::SDPA(const ov::Output<Node>& Q,
|
|||
, m_is_causal(is_causal)
|
||||
, m_output_type(output_type) {
|
||||
set_arguments({Q, K, V, attn_mask});
|
||||
set_causal(is_causal);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
|
|
@ -71,13 +73,23 @@ SDPA::SDPA(const ov::Output<Node>& Q,
|
|||
, m_is_causal(is_causal)
|
||||
, m_output_type(output_type) {
|
||||
set_arguments({Q, K, V, attn_mask, scale});
|
||||
set_causal(is_causal);
|
||||
validate_and_infer_types();
|
||||
}
|
||||
|
||||
std::shared_ptr<ov::Node> SDPA::clone_with_new_inputs(const ov::OutputVector& new_args) const {
|
||||
check_new_args_count(this, new_args);
|
||||
|
||||
return std::make_shared<SDPA>(new_args.at(0), new_args.at(1), new_args.at(2), m_order_q, m_order_k, m_order_v, m_order_out, m_is_causal, m_output_type);
|
||||
if (new_args.size() == 3) {
|
||||
return std::make_shared<SDPA>(new_args.at(0), new_args.at(1), new_args.at(2),
|
||||
m_order_q, m_order_k, m_order_v, m_order_out, m_is_causal, m_output_type);
|
||||
} else if (new_args.size() == 4) {
|
||||
return std::make_shared<SDPA>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3),
|
||||
m_order_q, m_order_k, m_order_v, m_order_out, m_is_causal, m_output_type);
|
||||
} else {
|
||||
return std::make_shared<SDPA>(new_args.at(0), new_args.at(1), new_args.at(2), new_args.at(3), new_args.at(4),
|
||||
m_order_q, m_order_k, m_order_v, m_order_out, m_is_causal, m_output_type);
|
||||
}
|
||||
}
|
||||
|
||||
void SDPA::validate_and_infer_types() {
|
||||
|
|
|
|||
|
|
@ -80,14 +80,7 @@ TransposeSDPAMatcher::TransposeSDPAMatcher() {
|
|||
ov::matcher_pass_callback callback = [OV_CAPTURE_CPY_AND_THIS](Matcher& m) {
|
||||
const auto& pattern_map = m.get_pattern_value_map();
|
||||
|
||||
std::shared_ptr<ov::op::v13::ScaledDotProductAttention> sdpa;
|
||||
if (pattern_map.find(sdpa_without_attn_mask_m) != pattern_map.end()) {
|
||||
sdpa = std::dynamic_pointer_cast<ov::op::v13::ScaledDotProductAttention>(pattern_map.at(sdpa_without_attn_mask_m).get_node_shared_ptr());
|
||||
} else if (pattern_map.find(sdpa_with_attn_mask_m) != pattern_map.end()) {
|
||||
sdpa = std::dynamic_pointer_cast<ov::op::v13::ScaledDotProductAttention>(pattern_map.at(sdpa_with_attn_mask_m).get_node_shared_ptr());
|
||||
} else if (pattern_map.find(sdpa_with_attn_mask_and_scale_m) != pattern_map.end()) {
|
||||
sdpa = std::dynamic_pointer_cast<ov::op::v13::ScaledDotProductAttention>(pattern_map.at(sdpa_with_attn_mask_and_scale_m).get_node_shared_ptr());
|
||||
}
|
||||
auto sdpa = std::dynamic_pointer_cast<ov::op::v13::ScaledDotProductAttention>(m.get_match_root());
|
||||
|
||||
if (!sdpa || transformation_callback(sdpa)) {
|
||||
return false;
|
||||
|
|
@ -101,33 +94,41 @@ TransposeSDPAMatcher::TransposeSDPAMatcher() {
|
|||
size_t input_k_output_idx = sdpa->get_input_source_output(1).get_index();
|
||||
size_t input_v_output_idx = sdpa->get_input_source_output(2).get_index();
|
||||
|
||||
if (pattern_map.count(transpose_q_m) > 0) {
|
||||
auto tranpose_a_order = std::dynamic_pointer_cast<ov::op::v0::Constant>(pattern_map.at(transpose_q_order_m).get_node_shared_ptr());
|
||||
order_q = tranpose_a_order->cast_vector<int64_t>();
|
||||
if (order_q.back() != static_cast<int64_t>(order_q.size() - 1)) // Allow any transposes without head_size dim position change
|
||||
auto process_transpose = [](const std::shared_ptr<Node>& transpose_node,
|
||||
const std::shared_ptr<Node>& transpose_order_const_node,
|
||||
std::vector<int64_t>& order,
|
||||
size_t& output_idx) {
|
||||
auto transpose_order_const = std::dynamic_pointer_cast<ov::op::v0::Constant>(transpose_order_const_node);
|
||||
|
||||
order = transpose_order_const->cast_vector<int64_t>();
|
||||
// Allow any transposes without head_size dim position change
|
||||
if (order.back() != static_cast<int64_t>(order.size() - 1))
|
||||
return false;
|
||||
|
||||
auto tranpose_a = std::dynamic_pointer_cast<ov::op::v1::Transpose>(pattern_map.at(transpose_q_m).get_node_shared_ptr());
|
||||
input_q_output_idx = tranpose_a->get_input_source_output(0).get_index();
|
||||
}
|
||||
if (pattern_map.count(transpose_k_m) > 0) {
|
||||
auto tranpose_b_order = std::dynamic_pointer_cast<ov::op::v0::Constant>(pattern_map.at(transpose_k_order_m).get_node_shared_ptr());
|
||||
order_k = tranpose_b_order->cast_vector<int64_t>();
|
||||
if (order_k.back() != static_cast<int64_t>(order_k.size() - 1)) // Allow any transposes without head_size dim position change
|
||||
return false;
|
||||
auto transpose = std::dynamic_pointer_cast<ov::op::v1::Transpose>(transpose_node);
|
||||
output_idx = transpose->get_input_source_output(0).get_index();
|
||||
|
||||
auto tranpose_b = std::dynamic_pointer_cast<ov::op::v1::Transpose>(pattern_map.at(transpose_k_m).get_node_shared_ptr());
|
||||
input_k_output_idx = tranpose_b->get_input_source_output(0).get_index();
|
||||
}
|
||||
if (pattern_map.count(transpose_v_m) > 0) {
|
||||
auto tranpose_c_order = std::dynamic_pointer_cast<ov::op::v0::Constant>(pattern_map.at(transpose_v_order_m).get_node_shared_ptr());
|
||||
order_v = tranpose_c_order->cast_vector<int64_t>();
|
||||
if (order_v.back() != static_cast<int64_t>(order_v.size() - 1)) // Allow any transposes without head_size dim position change
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
auto tranpose_c = std::dynamic_pointer_cast<ov::op::v1::Transpose>(pattern_map.at(transpose_k_m).get_node_shared_ptr());
|
||||
input_v_output_idx = tranpose_c->get_input_source_output(0).get_index();
|
||||
}
|
||||
bool can_fuse_transposes = true;
|
||||
if (pattern_map.count(transpose_q_m) > 0)
|
||||
can_fuse_transposes &= process_transpose(pattern_map.at(transpose_q_m).get_node_shared_ptr(),
|
||||
pattern_map.at(transpose_q_order_m).get_node_shared_ptr(),
|
||||
order_q, input_q_output_idx);
|
||||
|
||||
if (pattern_map.count(transpose_k_m) > 0)
|
||||
can_fuse_transposes &= process_transpose(pattern_map.at(transpose_k_m).get_node_shared_ptr(),
|
||||
pattern_map.at(transpose_k_order_m).get_node_shared_ptr(),
|
||||
order_k, input_k_output_idx);
|
||||
|
||||
if (pattern_map.count(transpose_v_m) > 0)
|
||||
can_fuse_transposes &= process_transpose(pattern_map.at(transpose_v_m).get_node_shared_ptr(),
|
||||
pattern_map.at(transpose_v_order_m).get_node_shared_ptr(),
|
||||
order_v, input_v_output_idx);
|
||||
|
||||
if (!can_fuse_transposes)
|
||||
return false;
|
||||
|
||||
auto input_q = ov::Output<Node>(pattern_map.at(input_q_m).get_node_shared_ptr(), input_q_output_idx);
|
||||
auto input_k = ov::Output<Node>(pattern_map.at(input_k_m).get_node_shared_ptr(), input_k_output_idx);
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "intel_gpu/plugin/transformations_pipeline.hpp"
|
||||
#include "intel_gpu/runtime/debug_configuration.hpp"
|
||||
#include "intel_gpu/runtime/itt.hpp"
|
||||
#include "low_precision/convolution.hpp"
|
||||
#include "low_precision/convolution_backprop_data.hpp"
|
||||
|
|
@ -307,6 +308,10 @@ void TransformationsPipeline::apply(std::shared_ptr<ov::Model> func) {
|
|||
manager.register_pass<ov::pass::CommonOptimizations>();
|
||||
|
||||
pass_config->set_callback<ov::pass::ScaledDotProductAttentionDecomposition>([&](const std::shared_ptr<const ov::Node> node){
|
||||
GPU_DEBUG_IF(cldnn::debug_configuration::get_instance()->enable_sdpa != -1) {
|
||||
GPU_DEBUG_CODE(return cldnn::debug_configuration::get_instance()->enable_sdpa == 1);
|
||||
}
|
||||
|
||||
if (!config.get_property(ov::intel_gpu::hint::enable_sdpa_optimization))
|
||||
return false;
|
||||
|
||||
|
|
|
|||
|
|
@ -171,6 +171,8 @@ static void print_help_messages() {
|
|||
message_list.emplace_back("OV_GPU_DisableDynamicImpl", "Disable dynamic implementation");
|
||||
message_list.emplace_back("OV_GPU_DisableRuntimeBufferFusing", "Disable runtime buffer fusing");
|
||||
message_list.emplace_back("OV_GPU_DisableMemoryReuse", "Disable memory reuse");
|
||||
message_list.emplace_back("OV_GPU_EnableSDPA", "This allows the enforcement of SDPA decomposition logic: 0 completely disables SDPA kernel usage, "
|
||||
"and 1 enables it for all the cases.");
|
||||
message_list.emplace_back("OV_GPU_DumpMemoryPool", "Dump memory pool contents of each iteration");
|
||||
message_list.emplace_back("OV_GPU_DumpMemoryPoolIters", "List of iterations to dump memory pool status, separated by space.");
|
||||
message_list.emplace_back("OV_GPU_DumpMemoryPoolPath", "Enable dumping memory pool status to csv file and set the dest path");
|
||||
|
|
@ -232,6 +234,7 @@ debug_configuration::debug_configuration()
|
|||
, serialize_compile(0)
|
||||
, max_kernels_per_batch(0)
|
||||
, impls_cache_capacity(-1)
|
||||
, enable_sdpa(-1)
|
||||
, disable_async_compilation(0)
|
||||
, disable_winograd_conv(0)
|
||||
, disable_dynamic_impl(0)
|
||||
|
|
@ -280,6 +283,7 @@ debug_configuration::debug_configuration()
|
|||
get_gpu_debug_env_var("ForceImplTypes", forced_impl_types_str);
|
||||
get_gpu_debug_env_var("MaxKernelsPerBatch", max_kernels_per_batch);
|
||||
get_gpu_debug_env_var("ImplsCacheCapacity", impls_cache_capacity);
|
||||
get_gpu_debug_env_var("EnableSDPA", enable_sdpa);
|
||||
get_gpu_debug_env_var("DisableAsyncCompilation", disable_async_compilation);
|
||||
get_gpu_debug_env_var("DisableWinogradConv", disable_winograd_conv);
|
||||
get_gpu_debug_env_var("DisableDynamicImpl", disable_dynamic_impl);
|
||||
|
|
|
|||
Loading…
Reference in New Issue