| """ | |
| Memory-efficient attention for decoding. | |
| It supports page size = 1. | |
| """ | |
| import functools | |
| import logging | |
| from wave_lang.kernel.lang.global_symbols import * | |
| from wave_lang.kernel.wave.compile import WaveCompileOptions, wave_compile | |
| from wave_lang.kernel.wave.constraints import GenericDot, MMAOperand, MMAType | |
| from wave_lang.kernel.wave.templates.paged_decode_attention import ( | |
| get_paged_decode_attention_kernels, | |
| get_paged_decode_intermediate_arrays_shapes, | |
| paged_decode_attention_shape, | |
| ) | |
| from wave_lang.kernel.wave.utils.general_utils import get_default_scheduling_params | |
| from wave_lang.kernel.wave.utils.run_utils import set_default_run_config | |
| logger = logging.getLogger(__name__) | |
| import os | |
| dump_generated_mlir = int(os.environ.get("WAVE_DUMP_MLIR", 0)) | |
| def get_wave_kernel( | |
| shape: paged_decode_attention_shape, | |
| max_kv_splits, | |
| input_dtype, | |
| output_dtype, | |
| logit_cap, | |
| ): | |
| mha = (shape.num_query_heads // shape.num_kv_heads) == 1 | |
| # Get the kernels (either compile or load from cache). | |
| if mha: | |
| mfma_variant = ( | |
| GenericDot(along_dim=MMAOperand.M, k_vec_size=4, k_mult=1), | |
| GenericDot(along_dim=MMAOperand.M, k_vec_size=1, k_mult=64), | |
| ) | |
| else: | |
| mfma_variant = (MMAType.F32_16x16x16_F16, MMAType.F32_16x16x16_F16) | |
| ( | |
| phase_0, | |
| phase_1, | |
| hyperparams_0, | |
| hyperparams_1, | |
| dynamic_symbols_0, | |
| dynamic_symbols_1, | |
| ) = get_paged_decode_attention_kernels( | |
| shape, | |
| mfma_variant, | |
| max_kv_splits, | |
| input_dtype=input_dtype, | |
| output_dtype=output_dtype, | |
| logit_cap=logit_cap, | |
| ) | |
| hyperparams_0.update(get_default_scheduling_params()) | |
| hyperparams_1.update(get_default_scheduling_params()) | |
| options = WaveCompileOptions( | |
| subs=hyperparams_0, | |
| canonicalize=True, | |
| run_bench=False, | |
| use_buffer_ops=True, | |
| waves_per_eu=2, | |
| dynamic_symbols=dynamic_symbols_0, | |
| wave_runtime=True, | |
| ) | |
| options = set_default_run_config(options) | |
| phase_0 = wave_compile(options, phase_0) | |
| options = WaveCompileOptions( | |
| subs=hyperparams_1, | |
| canonicalize=True, | |
| run_bench=False, | |
| use_buffer_ops=False, | |
| waves_per_eu=4, | |
| dynamic_symbols=dynamic_symbols_1, | |
| wave_runtime=True, | |
| ) | |
| options = set_default_run_config(options) | |
| phase_1 = wave_compile(options, phase_1) | |
| return phase_0, phase_1 | |
| def decode_attention_intermediate_arrays_shapes( | |
| num_seqs, head_size_kv, num_query_heads, max_kv_splits | |
| ): | |
| # Not all fields are used, but we need to pass them to the function | |
| shape = paged_decode_attention_shape( | |
| num_query_heads=num_query_heads, | |
| num_kv_heads=0, | |
| head_size=0, | |
| head_size_kv=head_size_kv, | |
| block_size=0, | |
| num_seqs=num_seqs, | |
| ) | |
| return get_paged_decode_intermediate_arrays_shapes(shape, max_kv_splits) | |
| def decode_attention_wave( | |
| q, | |
| k_buffer, | |
| v_buffer, | |
| o, | |
| b_req_idx, | |
| req_to_token, | |
| attn_logits, | |
| attn_logits_max, | |
| num_kv_splits, | |
| max_kv_splits, | |
| sm_scale, | |
| logit_cap, | |
| ): | |
| num_seqs, num_query_heads, head_size = q.shape | |
| _, num_kv_heads, _ = k_buffer.shape | |
| _, _, head_size_kv = v_buffer.shape | |
| block_size = 32 | |
| shape = paged_decode_attention_shape( | |
| num_query_heads, | |
| num_kv_heads, | |
| head_size, | |
| head_size_kv, | |
| block_size, | |
| num_seqs, | |
| ) | |
| phase_0, phase_1 = get_wave_kernel( | |
| shape, max_kv_splits, q.dtype, o.dtype, logit_cap | |
| ) | |
| mb_qk = phase_0( | |
| q, | |
| k_buffer, | |
| v_buffer, | |
| b_req_idx, | |
| req_to_token, | |
| attn_logits, | |
| attn_logits_max, | |
| ) | |
| if dump_generated_mlir: | |
| filename = f"wave_decode_attention_phase0_{'x'.join(map(str, shape))}.mlir" | |
| with open(filename, "w") as f: | |
| f.write(mb_qk.module_op.get_asm()) | |
| mb_sv = phase_1(attn_logits, attn_logits_max, b_req_idx, o) | |
| if dump_generated_mlir: | |
| filename = f"wave_decode_attention_phase1_{'x'.join(map(str, shape))}.mlir" | |
| with open(filename, "w") as f: | |
| f.write(mb_sv.module_op.get_asm()) | |
| def decode_attention_fwd( | |
| q, | |
| k_buffer, | |
| v_buffer, | |
| o, | |
| b_req_idx, | |
| req_to_token, | |
| attn_logits, | |
| attn_logits_max, | |
| num_kv_splits, | |
| max_kv_splits, | |
| sm_scale, | |
| logit_cap=0.0, | |
| ): | |
| decode_attention_wave( | |
| q, | |
| k_buffer, | |
| v_buffer, | |
| o, | |
| b_req_idx, | |
| req_to_token, | |
| attn_logits, | |
| attn_logits_max, | |
| num_kv_splits, | |
| max_kv_splits, | |
| sm_scale, | |
| logit_cap, | |
| ) | |
Xet Storage Details
- Size:
- 4.73 kB
- Xet hash:
- 4241b7821ec3726ff4a16a4a41f8120fdc8fbbdf3e708baef7b52aefba5b7481
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.