Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 45 additions & 17 deletions onnxruntime/contrib_ops/webgpu/bert/flash_attention.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ FlashAttentionProgram::FlashAttentionProgram(const std::string& kernel_name,
bool has_local_window,
uint32_t kv_cache_quantization_bits,
int compressed_head_size_u32,
bool use_seqlens_q)
bool use_seqlens_q,
bool has_softcap)
: Program{kernel_name},
has_attention_bias_(has_attention_bias),
is_qualcomm_(is_qualcomm),
Expand All @@ -191,7 +192,8 @@ FlashAttentionProgram::FlashAttentionProgram(const std::string& kernel_name,
kv_cache_quantization_(kv_cache_quantization_bits != 0),
kv_cache_quantization_bits_(kv_cache_quantization_bits),
compressed_head_size_u32_(compressed_head_size_u32),
use_seqlens_q_(use_seqlens_q) {
use_seqlens_q_(use_seqlens_q),
has_softcap_(has_softcap) {
}

Status SplitPackedQKVWithRotaryEmbeddingAndCopyKVProgram::GenerateShaderCode(ShaderHelper& sh) const {
Expand Down Expand Up @@ -404,22 +406,26 @@ Status FlashAttentionProgram::GenerateShaderCode(ShaderHelper& shader) const {
// Expectation is that present_key, and present_value contain past key and values since
// we are out of storage buffers a shader can have and both past/present cant be passed.
// The hidden size of each q head should be a multiple of 4 because shader uses vectorized loads.
shader.AddInput("q", ShaderUsage::UseUniform | ShaderUsage::UseValueTypeAlias | ShaderUsage::UseElementTypeAlias);
shader.AddInput("present_key", ShaderUsage::UseUniform);
shader.AddInput("present_value", ShaderUsage::UseUniform);
const auto& q = shader.AddInput("q", ShaderUsage::UseUniform | ShaderUsage::UseValueTypeAlias | ShaderUsage::UseElementTypeAlias);
const auto& present_key = shader.AddInput("present_key", ShaderUsage::UseUniform);
const auto& present_value = shader.AddInput("present_value", ShaderUsage::UseUniform);
const ShaderVariableHelper* attention_bias = nullptr;
if (has_attention_bias_) {
shader.AddInput("attention_bias", ShaderUsage::UseUniform);
attention_bias = &shader.AddInput("attention_bias", ShaderUsage::UseUniform);
}
const ShaderVariableHelper* seqlens_k = nullptr;
if (use_seqlen_k_) {
shader.AddInput("seqlens_k", ShaderUsage::None);
seqlens_k = &shader.AddInput("seqlens_k", ShaderUsage::None);
}
const ShaderVariableHelper* seqlens_q = nullptr;
if (use_seqlens_q_) {
shader.AddInput("seqlens_q", ShaderUsage::None);
seqlens_q = &shader.AddInput("seqlens_q", ShaderUsage::None);
}
const ShaderVariableHelper* head_sink = nullptr;
if (has_head_sink_) {
shader.AddInput("head_sink", ShaderUsage::UseUniform);
head_sink = &shader.AddInput("head_sink", ShaderUsage::UseUniform);
}
shader.AddOutput("output", ShaderUsage::UseUniform);
const auto& output = shader.AddOutput("output", ShaderUsage::UseUniform);

// Constant private-array indices improve NVIDIA's shared-memory path, but
// expanding larger heads can regress performance. The selection inputs
Expand Down Expand Up @@ -448,18 +454,28 @@ Status FlashAttentionProgram::GenerateShaderCode(ShaderHelper& shader) const {
WGSL_TEMPLATE_PARAMETER(has_attention_bias, has_attention_bias_),
WGSL_TEMPLATE_PARAMETER(has_head_sink, has_head_sink_),
WGSL_TEMPLATE_PARAMETER(has_local_window, has_local_window_),
WGSL_TEMPLATE_PARAMETER(has_softcap, has_softcap_),
WGSL_TEMPLATE_PARAMETER(is_qualcomm, is_qualcomm_),
WGSL_TEMPLATE_PARAMETER(is_unidirectional, is_unidirectional_),
WGSL_TEMPLATE_PARAMETER(kv_cache_quantization, kv_cache_quantization_),
WGSL_TEMPLATE_PARAMETER(max_k_step_param, max_k_step_),
WGSL_TEMPLATE_PARAMETER(prefer_subgroupshuffle, !is_nvidia_),
WGSL_TEMPLATE_PARAMETER(q_BNSH, q_BNSH_),
WGSL_TEMPLATE_PARAMETER(qc_cooperative, is_qualcomm_ && SubgroupSize() == 128),
WGSL_TEMPLATE_PARAMETER(qkv_head_size, qkv_head_size_),
WGSL_TEMPLATE_PARAMETER(qkv_num_heads, qkv_num_heads_),
WGSL_TEMPLATE_PARAMETER(unroll_shm_inner, unroll_shm_inner),
WGSL_TEMPLATE_PARAMETER(use_seqlen_k, use_seqlen_k_),
WGSL_TEMPLATE_PARAMETER(use_seqlens_q, use_seqlens_q_),
WGSL_TEMPLATE_PARAMETER(use_shm_path, use_shm_path_));
WGSL_TEMPLATE_PARAMETER(use_shm_path, use_shm_path_),
WGSL_TEMPLATE_OPTIONAL_VARIABLE(attention_bias, attention_bias),
WGSL_TEMPLATE_OPTIONAL_VARIABLE(head_sink, head_sink),
WGSL_TEMPLATE_VARIABLE(output, output),
WGSL_TEMPLATE_VARIABLE(present_key, present_key),
WGSL_TEMPLATE_VARIABLE(present_value, present_value),
WGSL_TEMPLATE_VARIABLE(q, q),
WGSL_TEMPLATE_OPTIONAL_VARIABLE(seqlens_k, seqlens_k),
WGSL_TEMPLATE_OPTIONAL_VARIABLE(seqlens_q, seqlens_q));
}

Status FlashAttentionPagedPrefillProgram::GenerateShaderCode(ShaderHelper& shader) const {
Expand Down Expand Up @@ -1228,7 +1244,8 @@ Status ApplyDensePrefill(const Tensor* Q, Tensor* present_key, Tensor* present_v
has_local_window,
kv_cache_quantization_bits,
compressed_head_size_u32,
use_seqlens_q};
use_seqlens_q,
parameters.softcap_ > 0.0f};
program.AddInputs({{Q, ProgramTensorMetadataDependency::TypeAndRank, 4},
{present_key, ProgramTensorMetadataDependency::TypeAndRank,
kv_cache_quantization_enabled ? 1 : 4},
Expand All @@ -1248,17 +1265,27 @@ Status ApplyDensePrefill(const Tensor* Q, Tensor* present_key, Tensor* present_v
}
program.AddOutputs({{attn_output, ProgramTensorMetadataDependency::TypeAndRank, 4}});

// On Apple GPUs, use a larger workgroup size to reduce barrier overhead.
const uint32_t prefill_tile_size = is_apple ? 128 : tile_size;
// Share each query across four lanes to reduce register pressure on Qualcomm.
const bool qc_cooperative = is_qualcomm && is_fp16 && has_subgroups &&
parameters.head_size_ == 128 && parameters.sequence_length_ >= 128 &&
!kv_cache_quantization_enabled &&
Comment thread
gyagp marked this conversation as resolved.
Resolved
Comment thread
gyagp marked this conversation as resolved.
Resolved
context.HasWGSLLanguageFeature(wgpu::WGSLLanguageFeatureName::SubgroupId) &&
context.HasFeature(wgpu::FeatureName::SubgroupSizeControl) &&
context.AdapterInfo().subgroupMinSize <= 128 && context.AdapterInfo().subgroupMaxSize >= 128;
const uint32_t prefill_tile_size = qc_cooperative ? 64 : (is_apple ? 128 : tile_size);
if (qc_cooperative) {
program.SetSubgroupSize(128);
program.SetUseSubgroupId(true);
Comment thread
gyagp marked this conversation as resolved.
Resolved
}
const uint32_t num_seq_tile = (parameters.sequence_length_ + prefill_tile_size - 1) / prefill_tile_size;

program.SetDispatchGroupSize(parameters.batch_size_ * parameters.num_heads_ * num_seq_tile)
.SetWorkgroupSize(prefill_tile_size)
.SetWorkgroupSize(qc_cooperative ? 256 : prefill_tile_size)
.CacheHint(has_attention_bias, parameters.head_size_, parameters.num_heads_,
parameters.is_unidirectional_, is_qualcomm, is_nvidia, is_apple,
has_subgroups, q_BNSH, use_seqlen_k, has_head_sink, has_local_window,
kv_cache_quantization_bits,
compressed_head_size_u32, program.max_k_step(), use_seqlens_q)
compressed_head_size_u32, program.max_k_step(), use_seqlens_q, parameters.softcap_ > 0.0f)
.AddUniformVariables({{static_cast<uint32_t>(parameters.sequence_length_)},
{static_cast<uint32_t>(parameters.total_sequence_length_)},
{static_cast<uint32_t>(present_sequence_length)},
Expand All @@ -1269,7 +1296,8 @@ Status ApplyDensePrefill(const Tensor* Q, Tensor* present_key, Tensor* present_v
{attn_bias_dim0},
{attn_bias_dim1},
{attn_bias_dim3},
{static_cast<uint32_t>(has_local_window ? local_window_size : 0)}});
{static_cast<uint32_t>(has_local_window ? local_window_size : 0)},
{parameters.softcap_}});

return context.RunProgram(program);
}
Expand Down
7 changes: 5 additions & 2 deletions onnxruntime/contrib_ops/webgpu/bert/flash_attention.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ class FlashAttentionProgram final : public Program<FlashAttentionProgram> {
bool has_local_window = false,
uint32_t kv_cache_quantization_bits = 0,
int compressed_head_size_u32 = 0,
bool use_seqlens_q = false);
bool use_seqlens_q = false,
bool has_softcap = false);

Status GenerateShaderCode(ShaderHelper& sh) const override;

Expand All @@ -121,7 +122,8 @@ class FlashAttentionProgram final : public Program<FlashAttentionProgram> {
{"attn_bias_dim0", ProgramUniformVariableDataType::Uint32},
{"attn_bias_dim1", ProgramUniformVariableDataType::Uint32},
{"attn_bias_dim3", ProgramUniformVariableDataType::Uint32},
{"local_window_size", ProgramUniformVariableDataType::Uint32});
{"local_window_size", ProgramUniformVariableDataType::Uint32},
{"softcap", ProgramUniformVariableDataType::Float32});

private:
bool has_attention_bias_;
Expand All @@ -143,6 +145,7 @@ class FlashAttentionProgram final : public Program<FlashAttentionProgram> {
// seqlens_q[b] and computes past_sequence_length_b = total_kv_b - q_len_b.
// When unset (default), callers keep the uniform-q_len clamp path unchanged.
bool use_seqlens_q_;
bool has_softcap_;
};

// Dedicated single-kernel prefill program for PagedAttention. It mirrors the
Expand Down
Loading
Loading