diff --git a/PyTorchSimDevice/csrc/aten/native/Extra.cpp b/PyTorchSimDevice/csrc/aten/native/Extra.cpp index eb76f5d7..7d776a5e 100644 --- a/PyTorchSimDevice/csrc/aten/native/Extra.cpp +++ b/PyTorchSimDevice/csrc/aten/native/Extra.cpp @@ -1,193 +1,212 @@ #include "Extra.h" -namespace at::native::openreg { - -at::Tensor quantize_per_tensor( - const at::Tensor& self, - double scale, - int64_t zero_point, - at::ScalarType dtype) { - return at::native::quantize_per_tensor(self, scale, zero_point, dtype); -} - -int64_t _fused_sdp_choice( - const at::Tensor& query, - const at::Tensor& key, - const at::Tensor& value, - const std::optional& attn_mask, - double dropout_p, - bool is_causal, - std::optional scale, - bool enable_gqa) { - - sdp::sdp_params params{query, key, value, attn_mask, dropout_p, is_causal, enable_gqa}; - - // Reject inputs that are fundamentally unsupported (e.g. wrong rank) - if (!sdp::check_tensor_shapes(params, /*debug=*/false)) { - return static_cast(sdp::SDPBackend::error); +namespace at::native::openreg +{ + + at::Tensor quantize_per_tensor( + const at::Tensor &self, + double scale, + int64_t zero_point, + at::ScalarType dtype) + { + return at::native::quantize_per_tensor(self, scale, zero_point, dtype); } - // q: (B, Hq, L, E) k/v: (B, H, S, E) - const int64_t Hq = query.size(-3); - const int64_t H = key.size(-3); - const int64_t L = query.size(-2); // query sequence length - const int64_t S = key.size(-2); // key/value sequence length - - // Conditions required by the MLIR FlashSDPA kernel: - // Prefill only : L == S (decode has L == 1, not supported) - // Non-GQA : Hq == H (equal query and KV heads) - // No dropout : template has no dropout implementation - // Dense tensors : no nested tensor support - const bool can_use_mlir_flash = - (L == S) && - (Hq == H) && !enable_gqa && - sdp::check_for_dropout(params, /*debug=*/false) && - sdp::check_nested_tensor(params, /*debug=*/false); - - const bool ctx_flash = at::globalContext().userEnabledFlashSDP(); - const bool ctx_math = at::globalContext().userEnabledMathSDP(); - - if (ctx_flash && can_use_mlir_flash) { - return static_cast(sdp::SDPBackend::overrideable); - } + int64_t _fused_sdp_choice( + const at::Tensor &query, + const at::Tensor &key, + const at::Tensor &value, + const std::optional &attn_mask, + double dropout_p, + bool is_causal, + std::optional scale, + bool enable_gqa) + { + + sdp::sdp_params params{query, key, value, attn_mask, dropout_p, is_causal, enable_gqa}; + + // Reject inputs that are fundamentally unsupported (e.g. wrong rank) + if (!sdp::check_tensor_shapes(params, /*debug=*/false)) + { + return static_cast(sdp::SDPBackend::error); + } + + // q: (B, Hq, L, E) k/v: (B, H, S, E) + const int64_t Hq = query.size(-3); + const int64_t H = key.size(-3); + const int64_t L = query.size(-2); // query sequence length + const int64_t S = key.size(-2); // key/value sequence length + + // Conditions required by the MLIR FlashSDPA kernel: + // No dropout : template has no dropout implementation + // Dense tensors : no nested tensor support + const bool can_use_mlir_flash = + sdp::check_for_dropout(params, /*debug=*/false) && + sdp::check_nested_tensor(params, /*debug=*/false); + + const bool ctx_flash = at::globalContext().userEnabledFlashSDP(); + const bool ctx_math = at::globalContext().userEnabledMathSDP(); + + if (ctx_flash && can_use_mlir_flash) + { + return static_cast(sdp::SDPBackend::overrideable); + } - return static_cast(sdp::SDPBackend::math); -} - -void quantize_tensor_per_tensor_affine_stub( - const at::Tensor& rtensor, - at::Tensor& qtensor, - double scale, - int64_t zero_point) {} - -std::tuple -_scaled_dot_product_fused_attention_overrideable_backward( - const at::Tensor& grad_out, - const at::Tensor& query, - const at::Tensor& key, - const at::Tensor& value, - const at::Tensor& attn_bias, - std::array grad_input_mask, - const at::Tensor& out, - const at::Tensor& logsumexp, - const at::Tensor& cum_seq_q, - const at::Tensor& cum_seq_k, - int64_t max_q, - int64_t max_k, - double dropout_p, - bool is_causal, - const at::Tensor& philox_seed, - const at::Tensor& philox_offset, - std::optional scale) { - return std::tuple( - at::empty_like(query), - at::empty_like(key), - at::empty_like(value), - at::empty_like(attn_bias)); -} - -namespace { -struct CustomAutogradFnReturnsSelf - : public torch::autograd::Function { - static at::Tensor forward( - torch::autograd::AutogradContext* ctx, - at::Tensor self) { - return self; + return static_cast(sdp::SDPBackend::math); } - static torch::autograd::variable_list backward( - torch::autograd::AutogradContext* ctx, - torch::autograd::variable_list grad_output) { - return {grad_output[0] * 0.5}; + void quantize_tensor_per_tensor_affine_stub( + const at::Tensor &rtensor, + at::Tensor &qtensor, + double scale, + int64_t zero_point) {} + + std::tuple + _scaled_dot_product_fused_attention_overrideable_backward( + const at::Tensor &grad_out, + const at::Tensor &query, + const at::Tensor &key, + const at::Tensor &value, + const at::Tensor &attn_bias, + std::array grad_input_mask, + const at::Tensor &out, + const at::Tensor &logsumexp, + const at::Tensor &cum_seq_q, + const at::Tensor &cum_seq_k, + int64_t max_q, + int64_t max_k, + double dropout_p, + bool is_causal, + const at::Tensor &philox_seed, + const at::Tensor &philox_offset, + std::optional scale) + { + return std::tuple( + at::empty_like(query), + at::empty_like(key), + at::empty_like(value), + at::empty_like(attn_bias)); } -}; - -struct CustomAutogradFnAliasing - : public torch::autograd::Function { - static at::Tensor forward( - torch::autograd::AutogradContext* ctx, - at::Tensor self) { - return self.view_symint(self.sym_sizes()); + + namespace + { + struct CustomAutogradFnReturnsSelf + : public torch::autograd::Function + { + static at::Tensor forward( + torch::autograd::AutogradContext *ctx, + at::Tensor self) + { + return self; + } + + static torch::autograd::variable_list backward( + torch::autograd::AutogradContext *ctx, + torch::autograd::variable_list grad_output) + { + return {grad_output[0] * 0.5}; + } + }; + + struct CustomAutogradFnAliasing + : public torch::autograd::Function + { + static at::Tensor forward( + torch::autograd::AutogradContext *ctx, + at::Tensor self) + { + return self.view_symint(self.sym_sizes()); + } + + static torch::autograd::variable_list backward( + torch::autograd::AutogradContext *ctx, + torch::autograd::variable_list grad_output) + { + return {grad_output[0] * 0.5}; + } + }; + } // namespace + + at::Tensor custom_autograd_fn_returns_self(at::Tensor x) + { + return CustomAutogradFnReturnsSelf::apply(x); } - static torch::autograd::variable_list backward( - torch::autograd::AutogradContext* ctx, - torch::autograd::variable_list grad_output) { - return {grad_output[0] * 0.5}; + at::Tensor custom_autograd_fn_aliasing(at::Tensor x) + { + return CustomAutogradFnAliasing::apply(x); } -}; -} // namespace - -at::Tensor custom_autograd_fn_returns_self(at::Tensor x) { - return CustomAutogradFnReturnsSelf::apply(x); -} - -at::Tensor custom_autograd_fn_aliasing(at::Tensor x) { - return CustomAutogradFnAliasing::apply(x); -} - -/* - This implementation is only used to test stub registration, so not all - capabilities are fully supported. - - Current Limitations: - - dtype: Float only - - input tensor: must be contiguous layout -*/ -// LITERALINCLUDE START: STUB ABS -void abs_kernel(at::TensorIteratorBase& iter) { - TORCH_CHECK(iter.ntensors() == 2, "Abs kernel expects 2 tensors"); - TORCH_CHECK( - iter.common_dtype() == at::ScalarType::Float, - "Abs kernel only supports float type"); - - auto& output_tensor = iter.tensor(0); - auto& input_tensor = iter.tensor(1); - - TORCH_CHECK( - input_tensor.sizes() == output_tensor.sizes(), - "Input and output tensor sizes must match."); - - auto abs_loop = [](float* out_ptr, const float* in_ptr, int64_t n) { - for (int64_t i = 0; i < n; ++i) { - out_ptr[i] = std::abs(in_ptr[i]); - } - }; - MemoryGuard guard(input_tensor, output_tensor); + /* + This implementation is only used to test stub registration, so not all + capabilities are fully supported. + + Current Limitations: + - dtype: Float only + - input tensor: must be contiguous layout + */ + // LITERALINCLUDE START: STUB ABS + void abs_kernel(at::TensorIteratorBase &iter) + { + TORCH_CHECK(iter.ntensors() == 2, "Abs kernel expects 2 tensors"); + TORCH_CHECK( + iter.common_dtype() == at::ScalarType::Float, + "Abs kernel only supports float type"); + + auto &output_tensor = iter.tensor(0); + auto &input_tensor = iter.tensor(1); - if (iter.is_contiguous()) { - abs_loop( - static_cast(iter.data_ptr(0)), - static_cast(iter.data_ptr(1)), - iter.numel()); - } else { TORCH_CHECK( - input_tensor.is_contiguous(), "Input tensor must be contiguous.") + input_tensor.sizes() == output_tensor.sizes(), + "Input and output tensor sizes must match."); + + auto abs_loop = [](float *out_ptr, const float *in_ptr, int64_t n) + { + for (int64_t i = 0; i < n; ++i) + { + out_ptr[i] = std::abs(in_ptr[i]); + } + }; + + MemoryGuard guard(input_tensor, output_tensor); + + if (iter.is_contiguous()) + { + abs_loop( + static_cast(iter.data_ptr(0)), + static_cast(iter.data_ptr(1)), + iter.numel()); + } + else + { + TORCH_CHECK( + input_tensor.is_contiguous(), "Input tensor must be contiguous.") - auto output = at::empty( - input_tensor.sizes(), - input_tensor.options().memory_format( - input_tensor.suggest_memory_format())); + auto output = at::empty( + input_tensor.sizes(), + input_tensor.options().memory_format( + input_tensor.suggest_memory_format())); - MemoryGuard guard(output); + MemoryGuard guard(output); - abs_loop( - static_cast(output.data_ptr()), - static_cast(iter.data_ptr(1)), - iter.numel()); + abs_loop( + static_cast(output.data_ptr()), + static_cast(iter.data_ptr(1)), + iter.numel()); - output_tensor.copy_(output); + output_tensor.copy_(output); + } } -} -// LITERALINCLUDE END: STUB ABS + // LITERALINCLUDE END: STUB ABS -at::Tensor& abs_out(const at::Tensor& self, at::Tensor& out) { - return at::native::abs_out(self, out); -} + at::Tensor &abs_out(const at::Tensor &self, at::Tensor &out) + { + return at::native::abs_out(self, out); + } -at::Tensor custom_abs(at::Tensor x) { - return at::abs(x); -} + at::Tensor custom_abs(at::Tensor x) + { + return at::abs(x); + } } // namespace at::native::openreg diff --git a/PyTorchSimFrontend/extension_codecache.py b/PyTorchSimFrontend/extension_codecache.py index 492133a3..841f9162 100644 --- a/PyTorchSimFrontend/extension_codecache.py +++ b/PyTorchSimFrontend/extension_codecache.py @@ -142,7 +142,7 @@ def load(cls, source_code, tog_path = os.path.join(write_path, "tile_graph.onnx") sample_mlir_path = new_input_path + "_sample" validation_binary_path = os.path.join(write_path, validation_binary_name) - gem5_cmds = mlir_gem5_compile_command(new_input_path, sample_mlir_path, raw_tog_path, vectorlane_size) + gem5_cmds = mlir_gem5_compile_command(new_input_path, sample_mlir_path, raw_tog_path, vectorlane_size, vlen=vlen) from filelock import FileLock os.makedirs(write_path, exist_ok=True) diff --git a/PyTorchSimFrontend/mlir/mlir_lowering.py b/PyTorchSimFrontend/mlir/mlir_lowering.py index 7f33d956..67779558 100644 --- a/PyTorchSimFrontend/mlir/mlir_lowering.py +++ b/PyTorchSimFrontend/mlir/mlir_lowering.py @@ -22,6 +22,10 @@ MLIRFlashSDPATemplate, flash_sdpa_args, calculate_scale, + _flash_decode_pad_query, + _flash_decode_slice_output, + _flash_gqa_decode_reshape_query, + _flash_gqa_decode_reshape_output ) from PyTorchSimFrontend import extension_config @@ -88,7 +92,6 @@ def _mlir_tuned_bmm(mat1, mat2, *, layout=None): return mlir_template.generate().output_node() - def _mlir_tuned_flash_sdpa( query : TensorBox, key : TensorBox, @@ -99,15 +102,41 @@ def _mlir_tuned_flash_sdpa( return_debug_mask : bool = False, scale : Optional[float] = None, enable_gqa : bool = False) -> tuple: - # _fused_sdp_choice in C++ already guarantees: - # L == S (prefill), Hq == H (non-GQA), dropout_p == 0.0 - # before routing here via SDPBackend::overrideable. - # Non-matching shapes fall back to SDPBackend::math in C++ and decompose - # into primitive ops (matmul/softmax) before reaching this lowering. scale = calculate_scale(query, scale) + + tile_l = extension_config.vpu_num_lanes + b = V.graph.sizevars.size_hint(query.get_size()[0]) + hq = V.graph.sizevars.size_hint(query.get_size()[1]) + h = V.graph.sizevars.size_hint(key.get_size()[1]) + l_real = V.graph.sizevars.size_hint(query.get_size()[2]) + + enable_gqa = (hq != h) and (hq % h == 0) + enable_decode = l_real == 1 + kv_ratio = hq // h + + if enable_gqa and enable_decode: + query = _flash_gqa_decode_reshape_query(query, tile_l, kv_ratio) + kv_ratio_orig = kv_ratio + hq = hq // kv_ratio_orig + l_real = l_real * kv_ratio_orig + kv_ratio = 1 + + query = _flash_decode_pad_query(query, tile_l, l_real) + elif not enable_gqa and enable_decode: + query = _flash_decode_pad_query(query, tile_l, 1) + N, Hq, H, L, S, E, Ev, layout, query, key, value = flash_sdpa_args(query, key, value) - mlir_template = MLIRFlashSDPATemplate([query, key, value], layout, scale) - return (mlir_template.generate().output_node(), None, None, None, None, None, None, None, None) + mlir_template = MLIRFlashSDPATemplate([query, key, value], layout, scale, kv_ratio, is_causal) + + out = mlir_template.generate().output_node() + + if enable_gqa and enable_decode: + out = _flash_decode_slice_output(out, l_real) + out = _flash_gqa_decode_reshape_output(out, l_real) + elif not enable_gqa and enable_decode: + out = _flash_decode_slice_output(out, 1) + + return (out, None, None, None, None, None, None, None, None) def conv_layout( diff --git a/PyTorchSimFrontend/mlir/mlir_sdpa_template.py b/PyTorchSimFrontend/mlir/mlir_sdpa_template.py index a3ae6192..46a7df6c 100644 --- a/PyTorchSimFrontend/mlir/mlir_sdpa_template.py +++ b/PyTorchSimFrontend/mlir/mlir_sdpa_template.py @@ -6,15 +6,69 @@ import torch from torch import empty_strided from torch._inductor.ir import IRNode, TensorBox, FixedLayout +from torch._inductor.lowering import lowerings from torch._inductor.virtualized import V from torch._inductor.select_algorithm import realize_inputs -from torch.backends.cuda import flash_sdp_enabled, mem_efficient_sdp_enabled from PyTorchSimFrontend import extension_config from PyTorchSimFrontend.mlir import mlir_common from PyTorchSimFrontend.mlir.mlir_template import MLIRTemplate from PyTorchSimFrontend.mlir.mlir_template import MLIRTemplateKernel +aten = torch.ops.aten + +def _flash_gqa_decode_reshape_query(query: TensorBox, tile_l: int, kv_ratio: int): + """Fold the kv_ratio GQA query heads into the L dim for decode (l == 1). + (B, hq, 1, e) -> (B, hq // kv_ratio, kv_ratio, e) == (B, h, kv_ratio, e), so + the kernel sees plain (non-GQA) attention of length kv_ratio. Returns the + reshaped query, kv_ratio=1 (kernel must not split heads again), and the + original kv_ratio (used to pad L and unfold the output). + """ + + if kv_ratio > tile_l: + raise NotImplementedError( + f"GQA-decode fold requires kv_ratio <= tile_l so the folded query " + f"heads fit one tile (kv_ratio={kv_ratio}, tile_l={tile_l})." + ) + + b = V.graph.sizevars.size_hint(query.get_size()[0]) + hq = V.graph.sizevars.size_hint(query.get_size()[1]) + e = V.graph.sizevars.size_hint(query.get_size()[3]) + + reshaped_query = lowerings[aten.reshape](query, [b, hq//kv_ratio, kv_ratio, e]) + + return reshaped_query + +def _flash_gqa_decode_reshape_output(output: TensorBox, kv_ratio_orig: int) -> TensorBox: + """Inverse of _flash_gqa_decode_reshape_query: unfold the L dim back into the + head dim, (B, h, kv_ratio, ev) -> (B, hq, 1, ev) with hq = h * kv_ratio. Run + after _flash_decode_slice_output has dropped the padding rows (L == kv_ratio). + The kept rows are interleaved with removed padding, so this is a real gather; + reshape (may copy), not a view. + """ + b = V.graph.sizevars.size_hint(output.get_size()[0]) + h = V.graph.sizevars.size_hint(output.get_size()[1]) + ev = V.graph.sizevars.size_hint(output.get_size()[3]) + hq = h * kv_ratio_orig + + return lowerings[aten.reshape](output, [b, hq, 1, ev]) + +def _flash_decode_pad_query(query: TensorBox, tile_l: int, real_l: int) -> TensorBox: + """Zero-pad the query length L up to tile_l so the flash kernel runs its + verified prefill path (query rows fill the vector lanes). query (n, hq, L, e) -> + (n, hq, tile_l, e); the extra rows are dropped again by _flash_decode_slice_output. + """ + pad_amt = tile_l - real_l + # constant_pad_nd pad spec is [e_lo, e_hi, l_lo, l_hi, ...]: + # pad the l dim at the high end by pad_amt. + return lowerings[aten.constant_pad_nd](query, [0, 0, 0, pad_amt], 0.0) + +def _flash_decode_slice_output(out: TensorBox, real_l: int) -> TensorBox: + """Slice the padded output (n, hq, tile_l, ev) back to the real L rows + (n, hq, L, ev) along the query dim. Lowers to a free reinterpret/view.""" + + # slice spec is [target, dim_idx, start_idx, len] + return lowerings[aten.slice.Tensor](out, len(out.get_size()) - 2, 0, real_l) def _make_offset_map_with_sym(strides, sym_dim, sym_stride, offset=0): """Like _make_offset_map but injects a block symbol ``s`` into dimension ``sym_dim``. @@ -109,10 +163,10 @@ def flash_sdpa_args( # e: embeding dimension of the query and key and ev: embeding dimension of the value. nq, hq, l, eq = query.get_size() nk, hk, sk, ek = key.get_size() - nk, hv, sv, ev = value.get_size() + nv, hv, sv, ev = value.get_size() n = V.graph.sizevars.guard_equals(nq, nk) - n = V.graph.sizevars.guard_equals(nq, nk) + n = V.graph.sizevars.guard_equals(nq, nv) h = V.graph.sizevars.guard_equals(hk, hv) s = V.graph.sizevars.guard_equals(sk, sv) @@ -125,14 +179,10 @@ def flash_sdpa_args( "Flash SDPA currently requires matching head dimensions between query and value (e == ev)." ) - # Minimal GQA support (single-batch only for now). # We map each query head to a KV head by grouping: hq = g * h. - if hq != h: - if n != 1: - raise NotImplementedError("Flash SDPA GQA is currently supported only for n == 1.") - if (hq % h) != 0: - raise NotImplementedError(f"Flash SDPA GQA requires hq % h == 0 (hq: {hq}, h: {h}).") - + if hq != h and (hq % h) != 0: + raise NotImplementedError(f"Flash SDPA GQA requires hq % h == 0 (hq: {hq}, h: {h}).") + layout = FixedLayout( query.get_device(), query.get_dtype(), @@ -151,7 +201,6 @@ def calculate_scale(query: torch.Tensor, scale: float) -> float: else: return scale - FLASH_SDPA_TEMPLATE = r""" // SDPA kernel // b = {{ b }} @@ -165,155 +214,176 @@ def calculate_scale(query: torch.Tensor, scale: float) -> float: func.func @{{ KERNEL_NAME }}{{kernel.def_kernel(inputs=[query, key, value], outputs=[out], names_str="query, key, value, out", input_reorder=input_reorder)}} { // Inputs - {{ kernel.def_sram_buffer("query", q_tile_desc, indent_size=2) }} - {{ kernel.def_sram_buffer("key", k_tile_desc, indent_size=2) }} - {{ kernel.def_sram_buffer("value", v_tile_desc, indent_size=2) }} + {{ kernel.def_sram_buffer("query", q_tile_desc, indent_size=2, dtype=io_stype) }} + {{ kernel.def_sram_buffer("key", k_tile_desc, indent_size=2, dtype=io_stype) }} + {{ kernel.def_sram_buffer("value", v_tile_desc, indent_size=2, dtype=io_stype) }} // Output - {{ kernel.def_sram_buffer("out", out_tile_desc, indent_size=2) }} + {{ kernel.def_sram_buffer("out", out_tile_desc, indent_size=2, dtype=io_stype) }} + + {% set mixed = io_stype != acc_stype %} + {% if mixed %} + {% set oacc_buffer = "oacc_buffer" %} + {{ kernel.def_sram_buffer("oacc", oacc_tile_desc, indent_size=2, dtype=acc_stype) }} + {% else %} + {% set oacc_buffer = "out_buffer" %} + {% endif %} // Intermediate buffers - {{ kernel.def_sram_buffer("mul", mul_tile_desc, indent_size=2) }} - {{ kernel.def_sram_buffer("max", max_desc, indent_size=2) }} - {{ kernel.def_sram_buffer("sum", sum_desc, indent_size=2) }} + {{ kernel.def_sram_buffer("mul", mul_tile_desc, indent_size=2, dtype=acc_stype) }} + {{ kernel.def_sram_buffer("max", max_desc, indent_size=2, dtype=acc_stype) }} + {{ kernel.def_sram_buffer("sum", sum_desc, indent_size=2, dtype=acc_stype) }} + + {% if mixed %} + {% set mul2_buffer = "mtrunc_buffer" %} + {{ kernel.def_sram_buffer("mtrunc", mtrunc_tile_desc, indent_size=2, dtype=io_stype) }} + {% else %} + {% set mul2_buffer = "mul_buffer" %} + {% endif %} // Constants - %c0 = arith.constant 0.0 : {{ data_stype }} - %c1 = arith.constant 1.0 : {{ data_stype }} - %c_scale = arith.constant {{ scale }} : {{ data_stype }} - %c_neg_inf = arith.constant -1.0e+30 : {{ data_stype }} + %c0 = arith.constant 0.0 : {{ acc_stype }} + %c1 = arith.constant 1.0 : {{ acc_stype }} + %c_scale = arith.constant {{ scale }} : {{ acc_stype }} + %c_neg_inf = arith.constant -1.0e+30 : {{ acc_stype }} - %v0_c = arith.constant dense<0.0> : vector<{{ chunk_size }}x{{ data_stype }}> - %v0_l = arith.constant dense<0.0> : vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ data_stype }}> - %v0_s = arith.constant dense<0.0> : vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ data_stype }}> - %v0_2x = arith.constant dense<0.0> : vector<2x{{ data_stype }}> + %v0_c = arith.constant dense<0.0> : vector<{{ chunk_size }}x{{ acc_stype }}> + %v0_l = arith.constant dense<0.0> : vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ acc_stype }}> + %v0_s = arith.constant dense<0.0> : vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ acc_stype }}> + %v0_2x = arith.constant dense<0.0> : vector<2x{{ acc_stype }}> - %v_neg_inf_c = arith.constant dense<-1.0e+30> : vector<{{ chunk_size }}x{{ data_stype }}> - %v_neg_inf_2x = arith.constant dense<-1.0e+30> : vector<2x{{ data_stype }}> + %v_neg_inf_c = arith.constant dense<-1.0e+30> : vector<{{ chunk_size }}x{{ acc_stype }}> + %v_neg_inf_2x = arith.constant dense<-1.0e+30> : vector<2x{{ acc_stype }}> - %v_scale = vector.broadcast %c_scale : {{ data_stype }} to vector<{{ tile_s }}x{{ data_stype }}> + %v_scale = vector.broadcast %c_scale : {{ acc_stype }} to vector<{{ tile_s }}x{{ acc_stype }}> {{ kernel.def_local_vars(indent_size=2) }} affine.for %index0 = 0 to {{ b }} { + %kv_index = affine.apply affine_map<(d0) -> (d0 floordiv {{ kv_ratio }})>(%index0) + affine.for %index3 = 0 to 1 step 1 { affine.for %index1 = 0 to {{ l }} step {{ tile_l }} { %q_dram_offset = affine.apply {{ q_offset_map }}(%index0, %index1, %index3) {{ kernel.def_dma_op("MVIN", "query", [], q_tile_desc, indent_size=8, dram_stride=q_dram_stride, dram_offset="q_dram_offset") }} - affine.vector_store %v0_l, %out_buffer[0, 0, 0] : {{ out_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ data_stype }}> - affine.vector_store %v_neg_inf_2x, %max_buffer[0, 0] : {{ max_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> - affine.vector_store %v0_2x, %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> + affine.vector_store %v0_l, %{{ oacc_buffer }}[0, 0, 0] : {{ out_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ acc_stype }}> + affine.vector_store %v_neg_inf_2x, %max_buffer[0, 0] : {{ max_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> + affine.vector_store %v0_2x, %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> - %qt_buffer2D = memref.reinterpret_cast %q_buffer to offset: [0], sizes: [{{ tile_e }}, {{ tile_l }}], strides: [{{ tile_l }}, 1] : {{ q_tile_desc.get_mlir_shape(data_stype) }} to memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1> - %ot_buffer2D = memref.reinterpret_cast %out_buffer to offset: [0], sizes: [{{ tile_e }}, {{ tile_l }}], strides: [{{ tile_l }}, 1] : {{ out_tile_desc.get_mlir_shape(data_stype) }} to memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1> + %qt_buffer2D = memref.reinterpret_cast %q_buffer to offset: [0], sizes: [{{ tile_e }}, {{ tile_l }}], strides: [{{ tile_l }}, 1] : {{ q_tile_desc.get_mlir_shape(io_stype) }} to memref<{{ tile_e }}x{{ tile_l }}x{{ io_stype }}, 1> + %ot_buffer2D = memref.reinterpret_cast %{{ oacc_buffer }} to offset: [0], sizes: [{{ tile_e }}, {{ tile_l }}], strides: [{{ tile_l }}, 1] : {{ out_tile_desc.get_mlir_shape(acc_stype) }} to memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1> affine.for %index2 = 0 to {{ s }} step {{ tile_s }} { - %k_dram_offset = affine.apply {{ k_offset_map }}(%index0, %index2, %index3) + %k_dram_offset = affine.apply {{ k_offset_map }}(%kv_index, %index2, %index3) {{ kernel.def_dma_op("MVIN", "key", [], k_tile_desc, indent_size=10, dram_stride=k_dram_stride, dram_offset="k_dram_offset") }} - %v_dram_offset = affine.apply {{ v_offset_map }}(%index0, %index2, %index3) + %v_dram_offset = affine.apply {{ v_offset_map }}(%kv_index, %index2, %index3) {{ kernel.def_dma_op("MVIN", "value", [], v_tile_desc, indent_size=10, dram_stride=v_dram_stride, dram_offset="v_dram_offset") }} - affine.vector_store %v0_s, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ data_stype }}> - - %k_buffer2D = memref.reinterpret_cast %k_buffer to offset: [0], sizes: [{{ tile_s }}, {{ tile_e }}], strides: [{{ tile_e }}, 1] : {{ k_tile_desc.get_mlir_shape(data_stype) }} to memref<{{ tile_s }}x{{ tile_e }}x{{ data_stype }}, 1> - %vt_buffer2D = memref.reinterpret_cast %v_buffer to offset: [0], sizes: [{{ tile_e }}, {{ tile_s }}], strides: [{{ tile_s }}, 1] : {{ v_tile_desc.get_mlir_shape(data_stype) }} to memref<{{ tile_e }}x{{ tile_s }}x{{ data_stype }}, 1> + affine.vector_store %v0_s, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ acc_stype }}> + %k_buffer2D = memref.reinterpret_cast %k_buffer to offset: [0], sizes: [{{ tile_s }}, {{ tile_e }}], strides: [{{ tile_e }}, 1] : {{ k_tile_desc.get_mlir_shape(io_stype) }} to memref<{{ tile_s }}x{{ tile_e }}x{{ io_stype }}, 1> + %vt_buffer2D = memref.reinterpret_cast %v_buffer to offset: [0], sizes: [{{ tile_e }}, {{ tile_s }}], strides: [{{ tile_s }}, 1] : {{ v_tile_desc.get_mlir_shape(io_stype) }} to memref<{{ tile_e }}x{{ tile_s }}x{{ io_stype }}, 1> // key @ query.t and scaling. linalg.matmul { idx_map = array } - ins(%k_buffer2D, %qt_buffer2D : memref<{{ tile_s }}x{{ tile_e }}x{{ data_stype }}, 1>, memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>) - outs(%mul_buffer : {{ mul_tile_desc.get_mlir_shape(data_stype) }}) - - %raw_mul_vec = affine.vector_load %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ tile_s }}x{{ data_stype }}> - %scaled_mul_vec = arith.mulf %raw_mul_vec, %v_scale : vector<{{ tile_s }}x{{ data_stype }}> - affine.vector_store %scaled_mul_vec, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ tile_s }}x{{ data_stype }}> + ins(%k_buffer2D, %qt_buffer2D : memref<{{ tile_s }}x{{ tile_e }}x{{ io_stype }}, 1>, memref<{{ tile_e }}x{{ tile_l }}x{{ io_stype }}, 1>) + outs(%mul_buffer : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}) + %raw_mul_vec = affine.vector_load %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ tile_s }}x{{ acc_stype }}> + %scaled_mul_vec = arith.mulf %raw_mul_vec, %v_scale : vector<{{ tile_s }}x{{ acc_stype }}> + affine.vector_store %scaled_mul_vec, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ tile_s }}x{{ acc_stype }}> // Find new max. - %old_max = affine.vector_load %max_buffer[0,0] : {{ max_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> + %old_max = affine.vector_load %max_buffer[0,0] : {{ max_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> - %chunk_max_res = affine.for %index5 = 0 to {{ tile_s }} step {{ chunk_size }} iter_args(%iter_max=%v_neg_inf_c) -> (vector<{{ chunk_size }}x{{ data_stype }}>) { - %chunk_val = affine.vector_load %mul_buffer[0, %index5] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ chunk_size }}x{{ data_stype }}> - %local_max = arith.maximumf %chunk_val, %iter_max : vector<{{ chunk_size }}x{{ data_stype }}> - affine.yield %local_max : vector<{{ chunk_size }}x{{ data_stype }}> + %chunk_max_res = affine.for %index5 = 0 to {{ tile_s }} step {{ chunk_size }} iter_args(%iter_max=%v_neg_inf_c) -> (vector<{{ chunk_size }}x{{ acc_stype }}>) { + %chunk_val = affine.vector_load %mul_buffer[0, %index5] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ chunk_size }}x{{ acc_stype }}> + %local_max = arith.maximumf %chunk_val, %iter_max : vector<{{ chunk_size }}x{{ acc_stype }}> + affine.yield %local_max : vector<{{ chunk_size }}x{{ acc_stype }}> } { accumulation_loop=true } - %max_cast = vector.shape_cast %chunk_max_res : vector<{{ chunk_size }}x{{ data_stype }}> to vector<{{ chunk_size // 2 }}x2x{{ data_stype }}> - %max_reduced_1 = vector.multi_reduction , %max_cast, %v_neg_inf_2x [0] : vector<8x2x{{ data_stype }}> to vector<2x{{ data_stype }}> - %max_shuffled = vector.shuffle %max_reduced_1, %max_reduced_1 [1, 0] : vector<2x{{ data_stype }}>, vector<2x{{ data_stype }}> - %max_reduced_2 = arith.maximumf %max_reduced_1, %max_shuffled : vector<2x{{ data_stype }}> - - %new_max = arith.maximumf %max_reduced_2, %old_max : vector<2x{{ data_stype }}> - affine.vector_store %new_max, %max_buffer[0, 0] : {{ max_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> + %max_cast = vector.shape_cast %chunk_max_res : vector<{{ chunk_size }}x{{ acc_stype }}> to vector<{{ chunk_size // 2 }}x2x{{ acc_stype }}> + %max_reduced_1 = vector.multi_reduction , %max_cast, %v_neg_inf_2x [0] : vector<{{ chunk_size // 2 }}x2x{{ acc_stype }}> to vector<2x{{ acc_stype }}> + %max_shuffled = vector.shuffle %max_reduced_1, %max_reduced_1 [1, 0] : vector<2x{{ acc_stype }}>, vector<2x{{ acc_stype }}> + %max_reduced_2 = arith.maximumf %max_reduced_1, %max_shuffled : vector<2x{{ acc_stype }}> + %new_max = arith.maximumf %max_reduced_2, %old_max : vector<2x{{ acc_stype }}> + affine.vector_store %new_max, %max_buffer[0, 0] : {{ max_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> // Compute rescale factors: exp(old_max - new_max) - %max_diff = arith.subf %old_max, %new_max : vector<2x{{ data_stype }}> - %max_diff_scalar = vector.extract %max_diff[0] : {{ data_stype }} from vector<2x{{ data_stype }}> - - %rescale_bcast_e = vector.broadcast %max_diff_scalar : {{ data_stype }} to vector<{{ tile_e }}x{{ data_stype }}> - %exp_rescale_e = math.exp %rescale_bcast_e : vector<{{ tile_e }}x{{ data_stype }}> + %max_diff = arith.subf %old_max, %new_max : vector<2x{{ acc_stype }}> + %max_diff_scalar = vector.extract %max_diff[0] : {{ acc_stype }} from vector<2x{{ acc_stype }}> - %rescale_bcast_2 = vector.broadcast %max_diff_scalar : {{ data_stype }} to vector<2x{{ data_stype }}> - %exp_rescale_2 = math.exp %rescale_bcast_2 : vector<2x{{ data_stype }}> + %rescale_bcast_e = vector.broadcast %max_diff_scalar : {{ acc_stype }} to vector<{{ tile_e }}x{{ acc_stype }}> + %exp_rescale_e = math.exp %rescale_bcast_e : vector<{{ tile_e }}x{{ acc_stype }}> + %rescale_bcast_2 = vector.broadcast %max_diff_scalar : {{ acc_stype }} to vector<2x{{ acc_stype }}> + %exp_rescale_2 = math.exp %rescale_bcast_2 : vector<2x{{ acc_stype }}> // Rescale previous out and sum accumulators - %old_out = affine.vector_load %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>, vector<{{ tile_e }}x{{ data_stype }}> - %rescaled_out = arith.mulf %exp_rescale_e, %old_out : vector<{{ tile_e }}x{{ data_stype }}> - affine.vector_store %rescaled_out, %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>, vector<{{ tile_e }}x{{ data_stype }}> - - %old_sum = affine.vector_load %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> - %rescaled_sum = arith.mulf %old_sum, %exp_rescale_2 : vector<2x{{ data_stype }}> + %old_out = affine.vector_load %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1>, vector<{{ tile_e }}x{{ acc_stype }}> + %rescaled_out = arith.mulf %exp_rescale_e, %old_out : vector<{{ tile_e }}x{{ acc_stype }}> + affine.vector_store %rescaled_out, %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1>, vector<{{ tile_e }}x{{ acc_stype }}> + %old_sum = affine.vector_load %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> + %rescaled_sum = arith.mulf %old_sum, %exp_rescale_2 : vector<2x{{ acc_stype }}> // Shift scores and apply exp: exp(x - new_max) - %scaled_scores_reload = affine.vector_load %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ tile_s }}x{{ data_stype }}> - %new_max_scalar = vector.extract %new_max[0] : {{ data_stype }} from vector<2x{{ data_stype }}> - %new_max_bcast = vector.broadcast %new_max_scalar : {{ data_stype }} to vector<{{ tile_s }}x{{ data_stype }}> - - %shifted_scores = arith.subf %scaled_scores_reload, %new_max_bcast : vector<{{ tile_s }}x{{ data_stype }}> - %exp_scores = math.exp %shifted_scores : vector<{{ tile_s }}x{{ data_stype }}> - affine.vector_store %exp_scores, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ tile_s }}x{{ data_stype }}> + %scaled_scores_reload = affine.vector_load %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ tile_s }}x{{ acc_stype }}> + %new_max_scalar = vector.extract %new_max[0] : {{ acc_stype }} from vector<2x{{ acc_stype }}> + %new_max_bcast = vector.broadcast %new_max_scalar : {{ acc_stype }} to vector<{{ tile_s }}x{{ acc_stype }}> + %shifted_scores = arith.subf %scaled_scores_reload, %new_max_bcast : vector<{{ tile_s }}x{{ acc_stype }}> + %exp_scores = math.exp %shifted_scores : vector<{{ tile_s }}x{{ acc_stype }}> + affine.vector_store %exp_scores, %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ tile_s }}x{{ acc_stype }}> // accumulate current sum - %chunk_sum_res = affine.for %index5 = 0 to {{ tile_s }} step {{ chunk_size }} iter_args(%iter_sum=%v0_c) -> (vector<{{ chunk_size }}x{{ data_stype }}>) { - %chunk_exp = affine.vector_load %mul_buffer[0, %index5] : {{ mul_tile_desc.get_mlir_shape(data_stype) }}, vector<{{ chunk_size }}x{{ data_stype }}> - %local_sum = arith.addf %chunk_exp, %iter_sum : vector<{{ chunk_size }}x{{ data_stype }}> - affine.yield %local_sum : vector<{{ chunk_size }}x{{ data_stype }}> + %chunk_sum_res = affine.for %index5 = 0 to {{ tile_s }} step {{ chunk_size }} iter_args(%iter_sum=%v0_c) -> (vector<{{ chunk_size }}x{{ acc_stype }}>) { + %chunk_exp = affine.vector_load %mul_buffer[0, %index5] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ chunk_size }}x{{ acc_stype }}> + %local_sum = arith.addf %chunk_exp, %iter_sum : vector<{{ chunk_size }}x{{ acc_stype }}> + affine.yield %local_sum : vector<{{ chunk_size }}x{{ acc_stype }}> } { accumulation_loop=true } - %zero_2x = vector.broadcast %c0 : {{ data_stype }} to vector<2x{{ data_stype }}> - %sum_cast = vector.shape_cast %chunk_sum_res : vector<{{ chunk_size }}x{{ data_stype }}> to vector<{{ chunk_size // 2 }}x2x{{ data_stype }}> - %sum_reduced_1 = vector.multi_reduction , %sum_cast, %zero_2x [0] : vector<8x2x{{ data_stype }}> to vector<2x{{ data_stype }}> - %sum_shuffled = vector.shuffle %sum_reduced_1, %sum_reduced_1 [1, 0] : vector<2x{{ data_stype }}>, vector<2x{{ data_stype }}> - %sum_reduced_2 = arith.addf %sum_reduced_1, %sum_shuffled : vector<2x{{ data_stype }}> - - %new_sum = arith.addf %sum_reduced_2, %rescaled_sum : vector<2x{{ data_stype }}> - affine.vector_store %new_sum, %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> + %zero_2x = vector.broadcast %c0 : {{ acc_stype }} to vector<2x{{ acc_stype }}> + %sum_cast = vector.shape_cast %chunk_sum_res : vector<{{ chunk_size }}x{{ acc_stype }}> to vector<{{ chunk_size // 2 }}x2x{{ acc_stype }}> + %sum_reduced_1 = vector.multi_reduction , %sum_cast, %zero_2x [0] : vector<{{ chunk_size // 2 }}x2x{{ acc_stype }}> to vector<2x{{ acc_stype }}> + %sum_shuffled = vector.shuffle %sum_reduced_1, %sum_reduced_1 [1, 0] : vector<2x{{ acc_stype }}>, vector<2x{{ acc_stype }}> + %sum_reduced_2 = arith.addf %sum_reduced_1, %sum_shuffled : vector<2x{{ acc_stype }}> + + %new_sum = arith.addf %sum_reduced_2, %rescaled_sum : vector<2x{{ acc_stype }}> + affine.vector_store %new_sum, %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> + + {% if mixed %} + %mul_full = affine.vector_load %mul_buffer[0, 0] : {{ mul_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ acc_stype }}> + %mtrunc_full = arith.truncf %mul_full : vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ acc_stype }}> to vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ io_stype }}> + affine.vector_store %mtrunc_full, %mtrunc_buffer[0, 0] : {{ mtrunc_tile_desc.get_mlir_shape(io_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_s, tile_l) }}x{{ io_stype }}> + {% endif %} - - // value.t @ mul linalg.matmul { idx_map = array } - ins(%vt_buffer2D, %mul_buffer : memref<{{ tile_e }}x{{ tile_s }}x{{ data_stype }}, 1>, {{ mul_tile_desc.get_mlir_shape(data_stype) }}) - outs(%ot_buffer2D : memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>) + ins(%vt_buffer2D, %{{ mul2_buffer }} : memref<{{ tile_e }}x{{ tile_s }}x{{ io_stype }}, 1>, {{ mul_tile_desc.get_mlir_shape(io_stype) }}) + outs(%ot_buffer2D : memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1>) } { accumulation_loop=true } // out @ row_sum^(-1) - %final_row_sum = affine.vector_load %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(data_stype) }}, vector<2x{{ data_stype }}> - %one_2x = vector.broadcast %c1 : {{ data_stype }} to vector<2x{{ data_stype }}> + %final_row_sum = affine.vector_load %sum_buffer[0, 0] : {{ sum_desc.get_mlir_shape(acc_stype) }}, vector<2x{{ acc_stype }}> + %one_2x = vector.broadcast %c1 : {{ acc_stype }} to vector<2x{{ acc_stype }}> + + %reciprocal_row_sum_2x = arith.divf %one_2x, %final_row_sum : vector<2x{{ acc_stype }}> + %reciprocal_scalar = vector.extract %reciprocal_row_sum_2x[0] : {{ acc_stype }} from vector<2x{{ acc_stype }}> + %reciprocal_bcast_e = vector.broadcast %reciprocal_scalar : {{ acc_stype }} to vector<{{ tile_e }}x{{ acc_stype }}> - %reciprocal_row_sum_2x = arith.divf %one_2x, %final_row_sum : vector<2x{{ data_stype }}> - %reciprocal_scalar = vector.extract %reciprocal_row_sum_2x[0] : {{ data_stype }} from vector<2x{{ data_stype }}> - %reciprocal_bcast_e = vector.broadcast %reciprocal_scalar : {{ data_stype }} to vector<{{ tile_e }}x{{ data_stype }}> + %accumulated_out = affine.vector_load %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1>, vector<{{ tile_e }}x{{ acc_stype }}> + %stable_final_out = arith.mulf %accumulated_out, %reciprocal_bcast_e : vector<{{ tile_e }}x{{ acc_stype }}> + affine.vector_store %stable_final_out, %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ acc_stype }}, 1>, vector<{{ tile_e }}x{{ acc_stype }}> - %accumulated_out = affine.vector_load %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>, vector<{{ tile_e }}x{{ data_stype }}> - %stable_final_out = arith.mulf %accumulated_out, %reciprocal_bcast_e : vector<{{ tile_e }}x{{ data_stype }}> - affine.vector_store %stable_final_out, %ot_buffer2D[0, 0] : memref<{{ tile_e }}x{{ tile_l }}x{{ data_stype }}, 1>, vector<{{ tile_e }}x{{ data_stype }}> + {% if mixed %} + %oacc_full = affine.vector_load %oacc_buffer[0, 0, 0] : {{ oacc_tile_desc.get_mlir_shape(acc_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ acc_stype }}> + %out_full = arith.truncf %oacc_full : vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ acc_stype }}> to vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ io_stype }}> + affine.vector_store %out_full, %out_buffer[0, 0, 0] : {{ out_tile_desc.get_mlir_shape(io_stype) }}, vector<{{ kernel.get_spad_size_per_lane(tile_l, tile_e) }}x{{ io_stype }}> + {% endif %} %out_dram_offset = affine.apply {{ out_offset_map }}(%index0, %index1, %index3) {{ kernel.def_dma_op("MVOUT", "out", [], out_tile_desc, indent_size=8, dram_stride=out_dram_stride, dram_offset="out_dram_offset") }} @@ -325,9 +395,11 @@ def calculate_scale(query: torch.Tensor, scale: float) -> float: """ class MLIRFlashSDPATemplate(MLIRTemplate): - def __init__(self, input_nodes, layout, scale, input_reorder=None): + def __init__(self, input_nodes, layout, scale, kv_ratio, is_causal, input_reorder=None): super().__init__("kernel", input_nodes, layout, input_reorder) self.scale = scale + self.kv_ratio = kv_ratio + self.is_causal = is_causal def render(self, kernel: MLIRTemplateKernel, @@ -369,7 +441,6 @@ def render(self, # (n, l, s, e, ev) loop_dim = [sympy.Symbol("index0"), sympy.Symbol("index1"), sympy.Symbol("index2"), sympy.Symbol("index3")] - # Hardware constraint: The tile split axis is restricted. # To accommodate this, we compute (key @ query.t) instead of (query @ key.t). # SRAM settings @@ -422,6 +493,10 @@ def render(self, # DRAM settings out_stride = out.get_layout().stride[1:] + oacc_tile_desc = mlir_common.MLIRMultiDimTile(out_tile_size, kernel.vector_lane, vlane_split_axis, vlane_stride) + oacc_tile_desc.set_tile_size_stride(out_tile_size, out_tile_stride) + oacc_tile_desc.set_name("oacc_buffer") + # Intermediate buffers # For mul = key @ query.t @@ -433,6 +508,12 @@ def render(self, mul_tile_desc.set_name("mul_buffer") #FIXME. What is the offset? -> It doesn't matter at this time. + # f16 (io) copy of mul, used as the second matmul's input in mixed precision: + # the SA input must be io dtype, but mul is the f32 accumulator of matmul 1. + mtrunc_tile_desc = mlir_common.MLIRMultiDimTile(mul_tile_size, kernel.vector_lane, vlane_split_axis, vlane_stride) + mtrunc_tile_desc.set_tile_size_stride(mul_tile_size, mul_tile_stride) + mtrunc_tile_desc.set_name("mtrunc_buffer") + # For storing maximum values per row vlane_split_axis = 0 max_size = [tile_l, 2] @@ -450,7 +531,9 @@ def render(self, sum_desc.set_name("sum_buffer") # For reduction - chunk_size = 16 + io_stype = mlir_common.DTYPE_TO_MLIR[query.get_dtype()] + acc_stype = "f32" + chunk_size = max(2, int(kernel.vector_lane / 32 if acc_stype == "f32" else 16)) # DMA strides and offset affine maps (dram_stride + dram_offset style) q_dram_stride = [int(q_stride[0]), int(q_stride[1]), int(q_stride[2])] @@ -476,7 +559,8 @@ def render(self, tile_l = tile_l, tile_s = tile_s, tile_e = tile_e, # Tile sizes (sram) - data_stype="f32", + io_stype = io_stype, + acc_stype = acc_stype, query = query, key = key, value = value, @@ -493,11 +577,15 @@ def render(self, k_tile_desc = k_tile_desc, v_tile_desc = v_tile_desc, mul_tile_desc = mul_tile_desc, + mtrunc_tile_desc = mtrunc_tile_desc, out_tile_desc = out_tile_desc, # Tile descriptions (sram) + oacc_tile_desc = oacc_tile_desc, max_desc = max_desc, sum_desc = sum_desc, # Intermediate buffer descriptions (sram) scale = self.scale, chunk_size = chunk_size, + kv_ratio = self.kv_ratio, + is_causal = self.is_causal, input_reorder = self.input_reorder # ETC ) @@ -535,12 +623,14 @@ def extract_info(self, template_buffer_node, epilogue_nodes, prologue_nodes): # Reuse the existing function in MLIRBMMTemplate. def select_tile(self, kernel, l, s, e, n_extra_node, n_extra_read, n_prologue_node): - # FIXME: Update the method for getting tile candidates once TestDmaFineGrained oass works correctly with Flash Attention. + # FIXME: Update the method for getting tile candidates once TestDmaFineGrained pass works correctly with Flash Attention. # tile_candidates = kernel.flash_sdpa_mapping(l, s, e, n_extra_node=n_extra_node) - tile_candidates = [[kernel.vector_lane, kernel.vector_lane, e]] + tile_l = min(kernel.vector_lane, l) + tile_s = min(kernel.vector_lane, s) + tile_candidates = [[tile_l, tile_s, e]] for idx, (tile_l, tile_s, tile_e) in enumerate(tile_candidates): - subtile_l = tile_l if (tile_l < kernel.vector_lane) or n_prologue_node else kernel.vector_lane + subtile_l = tile_l # if (tile_l < kernel.vector_lane) or n_prologue_node else kernel.vector_lane subtile_s = tile_s # if (tile_s < kernel.vector_lane) or prologue_nodes else kernel.vector_lane subtile_e = tile_e # if (tile_e < kernel.vector_lane) or prologue_nodes else kernel.vector_lane diff --git a/PyTorchSimFrontend/mlir/mlir_template.py b/PyTorchSimFrontend/mlir/mlir_template.py index 529a49b5..e9c13b14 100644 --- a/PyTorchSimFrontend/mlir/mlir_template.py +++ b/PyTorchSimFrontend/mlir/mlir_template.py @@ -968,15 +968,22 @@ def generate_dma_code(): self.render_hooks[key] = (priority, generate_dma_code) return key - def def_sram_buffer(self, dram_name, tile_desc, id=0, indent_size=0): + def def_sram_buffer(self, dram_name, tile_desc, id=0, indent_size=0, dtype=None): # Prepare code block with self: - try: - dtype = self.named_nodes[dram_name].get_layout().dtype - except (KeyError, AttributeError, TypeError): - import torch - dtype = torch.float32 - + if dtype is not None: + # Explicit dtype: accept an MLIR type string ("f16"/"f32") or a torch + # dtype. Used for intermediate buffers with no DRAM node to infer from + # (e.g. the acc buffers max/sum forced to f32). + if isinstance(dtype, str): + dtype = mlir_common.MLIR_TO_DTYPE[dtype] + else: + try: + dtype = self.named_nodes[dram_name].get_layout().dtype + except (KeyError, AttributeError, TypeError): + import torch + dtype = torch.float32 + tile_shape = tile_desc.get_mlir_shape(mlir_common.DTYPE_TO_MLIR[dtype]) buffer_name = self.allocate_sram_buffer(dtype, dram_name, tile_desc, id, forced_name=dram_name) code = f"%{tile_desc.name} = memref.get_global @{buffer_name} : {tile_shape}" diff --git a/PyTorchSimFrontend/mlir/passes/lower_to_vcix.py b/PyTorchSimFrontend/mlir/passes/lower_to_vcix.py index ac93ebc8..bc54da6c 100644 --- a/PyTorchSimFrontend/mlir/passes/lower_to_vcix.py +++ b/PyTorchSimFrontend/mlir/passes/lower_to_vcix.py @@ -340,17 +340,19 @@ def _lower_matmul(op, SS, vlen): from mlir.dialects import arith A, B, C = op.operands[0], op.operands[1], op.operands[2] - mtA, mtB = ir.MemRefType(A.type), ir.MemRefType(B.type) - elt = mtA.element_type + mtA, mtB, mtC = ir.MemRefType(A.type), ir.MemRefType(B.type), ir.MemRefType(C.type) + eltA, eltB, eltC = mtA.element_type, mtB.element_type, mtC.element_type M, K, N = mtA.shape[0], mtA.shape[1], mtB.shape[1] + # Mirror the C++ guard: a dimension > SS must be an exact multiple, else the # N//SS / K//SS loop trip counts below silently drop the tail tile. for _dim, _name in ((M, "M"), (N, "N"), (K, "K")): if _dim > SS and _dim % SS != 0: raise NotImplementedError( f"matmul {_name}={_dim} must be a multiple of systolic size {SS} when > {SS}") - elen = _elt_bits(elt) - nr_element = vlen // elen + + elenA, elenB, elenC = _elt_bits(eltA), _elt_bits(eltB), _elt_bits(eltC) + nr_eltA, nr_eltB, nr_eltC = vlen // elenA, vlen // elenB, vlen // elenC i64 = ir.IntegerType.get_signless(64) def a64(v): return ir.IntegerAttr.get(i64, v) @@ -362,9 +364,14 @@ def a64(v): return ir.IntegerAttr.get(i64, v) if is_conv2d: # inner = [k_h, k_w, o_h, o_w] tile_kw, tile_oh, tile_ow = inner[1], inner[2], inner[3] - vectorType = ir.VectorType.get([nr_element], elt) - nr_m = max(min(M, nr_element), 2) - vectorMType = ir.VectorType.get([nr_m], elt) + vectorTypeA = ir.VectorType.get([nr_eltA], eltA) + vectorTypeC = ir.VectorType.get([nr_eltC], eltC) + + nr_m = max(min(M, nr_eltA), 2) + vectorMTypeA = ir.VectorType.get([nr_m], eltA) + + nr_m_c = max(min(M, nr_eltC), 2) + vectorMTypeC = ir.VectorType.get([nr_m_c], eltC) spad_map, spadX, spadY = _spad_maps() idxMap = [0, 1, 2] @@ -445,11 +452,13 @@ def _root(v): if is_conv2d else ir.InsertionPoint(op)) with ip: c0 = _const_index(0) - rvl = arith.ConstantOp(i64, a64(nr_element)).result + rvl = arith.ConstantOp(i64, a64(nr_eltA)).result + rvl_c = arith.ConstantOp(i64, a64(nr_eltC)).result K_val, N_val, M_val = _const_index(K), _const_index(N), _const_index(M) push_val = _const_index(push_length) num1 = _const_index(1) - zero_pad = arith.ConstantOp(elt, ir.FloatAttr.get(elt, 0.0)).result + zero_pad = arith.ConstantOp(eltA, ir.FloatAttr.get(eltA, 0.0)).result + zero_pad_c = arith.ConstantOp(eltC, ir.FloatAttr.get(eltC, 0.0)).result # --- inner N / K loops --- from mlir.dialects import affine @@ -478,8 +487,8 @@ def _root(v): nk_inner = kl else: with body_ip: - zv = ir.DenseElementsAttr.get_splat(vectorType, ir.FloatAttr.get(elt, 0.0)) - zero_vector = arith.ConstantOp(vectorType, zv).result + zv = ir.DenseElementsAttr.get_splat(vectorTypeA, ir.FloatAttr.get(eltA, 0.0)) + zero_vector = arith.ConstantOp(vectorTypeA, zv).result n_tag = c0 if N == subtileN else n_idx k_tag = c0 if K == subtileK else k_idx @@ -509,12 +518,12 @@ def _root(v): _dma_wait(BTag, btag_idx, num1) # --- weight push loop (K x N) --- - for i in range(0, SS, nr_element): + for i in range(0, SS, nr_eltA): if i < K: sp = _apply(spad_map, [n_idx, k_idx, _const_index(i), K_val, _const_index(SS)]) wx = _apply(spadX, [sp, N_val]) wy = _apply(spadY, [sp, N_val]) - wv = _transfer_read(vectorType, B, [wx, wy], zero_pad) + wv = _transfer_read(vectorTypeA, B, [wx, wy], zero_pad) else: wv = zero_vector _vcix("vcix.iv", [wv, rvl], [], @@ -581,28 +590,28 @@ def _root(v): _dma_wait(BiasTag, bias_tag_idx, num1) # --- input push loop (M x K) --- - for i in range(0, M_LOOP, nr_element): + for i in range(0, M_LOOP, nr_eltA): sp = _apply(spad_map, [k_idx, m_idx, _const_index(i), M_val, push_val]) x = _apply(spadX, [sp, K_val]) y = _apply(spadY, [sp, K_val]) - iv = _transfer_read(vectorMType, A, [x, y], zero_pad) + iv = _transfer_read(vectorMTypeA, A, [x, y], zero_pad) _vcix("vcix.iv", [iv, rvl], [], {"opcode": a64(0), "imm": a64(0), "rd": a64(0)}) # --- compute --- _vcix("vcix.i", [rvl], [], {"opcode": a64(1), "imm": a64(4), "rd": a64(0), "rs2": a64(0), - "sew": a64(elen), "lmul": a64(0)}) + "sew": a64(elenA), "lmul": a64(0)}) # --- pop loop (M x N) --- - for i in range(0, M_LOOP, nr_element): + for i in range(0, M_LOOP, nr_eltC): sp = _apply(spad_map, [n_idx, m_idx, _const_index(i), M_val, push_val]) - vpop = _vcix("vcix.v.i", [rvl], [vectorMType], + vpop = _vcix("vcix.v.i", [rvl_c], [vectorMTypeC], {"opcode": a64(2), "imm": a64(0), "rs2": a64(0)}).results[0] x = _apply(spadX, [sp, N_val]) y = _apply(spadY, [sp, N_val]) - prev = _transfer_read(vectorMType, C, [x, y], zero_pad) - if ir.IntegerType.isinstance(elt): + prev = _transfer_read(vectorMTypeC, C, [x, y], zero_pad_c) + if ir.IntegerType.isinstance(eltC): out = arith.AddIOp(prev, vpop).result else: out = arith.AddFOp(prev, vpop).result diff --git a/Simulator/simulator.py b/Simulator/simulator.py index 2b9f05be..9d50d1c6 100644 --- a/Simulator/simulator.py +++ b/Simulator/simulator.py @@ -144,7 +144,8 @@ def run_spike(self, args, arg_attributes, runtime_path, binary, vectorlane_size= base_path= f"--base-path={runtime_path}" os.makedirs(os.path.join(runtime_path, "indirect_access"), exist_ok=True) os.makedirs(os.path.join(runtime_path, "dma_access"), exist_ok=True) - run = f'spike --isa rv64gcv_zfh --varch=vlen:256,elen:64 {vectorlane_option} {spad_option} {kernel_address} {base_path} /workspace/riscv-pk/build/pk {target_binary} {file_path_str}' + spike_vlen = extension_config.vpu_vector_length_bits + run = f'spike --isa rv64gcv_zfh --varch=vlen:{spike_vlen},elen:64 {vectorlane_option} {spad_option} {kernel_address} {base_path} /workspace/riscv-pk/build/pk {target_binary} {file_path_str}' if not silent_mode: logger.debug(f"[Spike] cmd> {run}") logger.info("[Spike] Running Spike simulator") diff --git a/configs/ramulator2_configs/HBM3_TPUv6e.yaml b/configs/ramulator2_configs/HBM3_TPUv6e.yaml new file mode 100644 index 00000000..57f359f9 --- /dev/null +++ b/configs/ramulator2_configs/HBM3_TPUv6e.yaml @@ -0,0 +1,45 @@ +{ + "frontend": { + "impl": "External", + "clock_ratio": 1 + }, + "memory_system": { + "impl": "GenericDRAM", + "clock_ratio": 1, + "channel_mapper": { + "impl": "PassThroughChannelMapper" + }, + "controllers": [{ + "impl": "HBM", + "wr_low_watermark": 0.2, + "wr_high_watermark": 0.8, + "read_buffer_size": 64, + "write_buffer_size": 64, + "priority_buffer_size": 1568, + "scheduler": { + "impl": "FRFCFS" + }, + "refresh_manager": { + "impl": "AllBank", + "scope": "PseudoChannel" + }, + "row_policy": { + "impl": "Open" + }, + "addr_mapper": { + "impl": "RoBaRaCoCh" + }, + "dram": { + "impl": "HBM3", + "org": { + "dq": 32, + "count": [1, 2, 4, 4, 32768, 32] + }, + "timing": [6400, 4, 52, 52, 28, 52, 96, 148, 48, 12, 16, 4, 8, 16, 16, 16, 24, 48, 4, 4, 1120, 640, 1120, 640, 26, 12480, 390, 312], + "channel_width": 32, + "read_latency": 56, + "timing_constraints": [[0, [0], [0, 1, 2, 7, 8, 9, 10], 3], [0, [3, 4, 5, 6], [3, 4, 5, 6], 2], [1, [3, 5], [3, 5], 4], [1, [4, 6], [4, 6], 4], [1, [3, 5], [3, 5], 4], [1, [4, 6], [4, 6], 4], [1, [3, 5], [4, 6], 44], [1, [4, 6], [3, 5], 36], [1, [3], [2], 13], [1, [4], [2], 69], [1, [0], [0], 16], [1, [0], [0], 48, 4], [1, [0], [2], 98], [1, [2], [0], 50], [1, [1], [1], 4], [1, [0], [7], 150], [1, [1, 2], [7], 52], [1, [5], [7], 65], [1, [6], [7], 121], [1, [7], [0], 1118], [1, [7], [2], 1120], [1, [8], [0], 24], [1, [0], [8], 18], [1, [0], [9], 150], [1, [1, 2], [9], 52], [1, [5], [9], 65], [1, [6], [9], 121], [1, [9], [0], 1118], [1, [9], [2], 1120], [1, [10], [0], 24], [1, [0], [10], 18], [2, [3, 5], [3, 5], 8], [2, [4, 6], [4, 6], 8], [2, [4, 6], [3, 5], 44], [2, [0], [0], 16], [3, [0], [0], 148], [3, [0], [3, 5], 53], [3, [0], [4, 6], 29], [3, [0], [1], 98], [3, [1], [0], 50], [3, [3], [1], 13], [3, [4], [1], 69], [3, [5], [0], 63], [3, [6], [0], 119], [3, [8], [0], 638], [3, [0], [8], 150], [3, [1], [8], 52], [3, [10], [0], 638], [3, [0], [10], 150], [3, [1], [10], 52]] + } + }] + } +} \ No newline at end of file diff --git a/configs/ramulator2_configs/gen_configs.py b/configs/ramulator2_configs/gen_configs.py index 65721f28..bf077957 100644 --- a/configs/ramulator2_configs/gen_configs.py +++ b/configs/ramulator2_configs/gen_configs.py @@ -106,6 +106,10 @@ def gen_hbm2_tpuv2(): dram = ramulator.dram.HBM2(org_preset="HBM2_8Gb", timing_preset="HBM2_1400Mbps") return make_config(dram, clock_ratio=1, refresh_scope="PseudoChannel") +def gen_hbm3_tpuv6e(): + dram = ramulator.dram.HBM3(org_preset="HBM3_8Gb", timing_preset="HBM3_6400Mbps") + return make_config(dram, clock_ratio=1, refresh_scope="PseudoChannel") + def gen_ddr4(): # Available timing presets — check python/ramulator/dram/ddr4.py dram = ramulator.dram.DDR4(org_preset="DDR4_8Gb_x8", timing_preset="DDR4_3200AA") @@ -128,6 +132,7 @@ def gen_lpddr5x(): "HBM2_TPUv4.yaml": gen_hbm2_tpuv4, "HBM2_TPUv3.yaml": gen_hbm2_tpuv3, "HBM2_TPUv2.yaml": gen_hbm2_tpuv2, + "HBM3_TPUv6e.yaml": gen_hbm3_tpuv6e, "DDR4.yaml": gen_ddr4, "LPDDR5.yaml": gen_lpddr5, "LPDDR5X.yaml": gen_lpddr5x, diff --git a/configs/systolic_ws_256x256_c1_simple_noc_tpuv6e.yml b/configs/systolic_ws_256x256_c1_simple_noc_tpuv6e.yml new file mode 100644 index 00000000..40677fc8 --- /dev/null +++ b/configs/systolic_ws_256x256_c1_simple_noc_tpuv6e.yml @@ -0,0 +1,29 @@ +num_cores: 1 +core_freq_mhz: 3502 +core_stats_print_period_cycles: 200 +num_systolic_array_per_core: 2 + +vpu_num_lanes: 256 +vpu_spad_size_kb_per_lane: 512 +vpu_vector_length_bits: 512 + +dram_type: ramulator2 +dram_channels: 32 +dram_stats_print_period_cycles: 200 +ramulator_config_path: ../configs/ramulator2_configs/HBM3_TPUv6e.yaml +l2d_type: datacache +l2d_config: S:128:128:512,32,L:T:m:W:L,A:192:4,32:0,32 + +icnt_type: simple +icnt_latency_cycles: 10 +icnt_freq_mhz: 3502 +icnt_injection_ports_per_core: 16 + +pytorchsim_functional_mode: 1 +pytorchsim_timing_mode: 1 + +codegen_mapping_strategy: autotune +codegen_external_mapping_file: '' +codegen_autotune_max_retry: 10 +codegen_autotune_template_topk: 4 +codegen_compiler_optimization: all diff --git a/scripts/analyze_sdpa_v6e.py b/scripts/analyze_sdpa_v6e.py new file mode 100644 index 00000000..9e6bbe2e --- /dev/null +++ b/scripts/analyze_sdpa_v6e.py @@ -0,0 +1,110 @@ +"""Compute the aggregates for the v6e SDPA characterization dashboard. + +Reads the enriched measurement CSV produced directly by measure_sdpa_v6e.py +(cycles, SA/VPU utilization, DRAM bandwidth AND per-case DMA metrics already +in-file) and emits a compact JSON (data for the HTML report). This script no +longer parses TOGSim logs or writes the enriched CSV -- that is now the +measurement harness's job.""" +import csv, os, json, statistics as st + +BASE = "/workspace/PyTorchSim" +SRC = os.path.join(BASE, "sdpa_results", "v6e_gem5_measure_enriched.csv") +JOUT = os.path.join(BASE, "sdpa_results", "v6e_report_data.json") +PEAK_BW = 1638.4 # GB/s (HBM3 32ch) + +def _int(s): + s = str(s).strip() + return int(s) if s not in ("", "None") else None + +def _float(s): + s = str(s).strip() + return float(s) if s not in ("", "None") else None + +# Keep only successfully-measured cases (error rows have a blank total_cycles). +rows = [r for r in csv.DictReader(open(SRC)) if str(r.get("total_cycles", "")).strip()] +for r in rows: + r["c"] = int(r["total_cycles"]); r["Hq"] = int(r["Hq"]); r["Hkv"] = int(r["Hkv"]); r["B"] = int(r["B"]) + r["S"] = int(r["S"]); r["D"] = int(r["D"]) + r["sa_avg"] = (float(r["sa0_util"]) + float(r["sa1_util"])) / 2 + r["vpu"] = float(r["vpu_util"]); r["bw"] = float(r["dram_bw_gbs"]) + r["dram_util"] = 100.0 * r["bw"] / PEAK_BW + # DMA metrics come straight from the enriched CSV (no log re-parsing). + r["dma_active"] = _int(r.get("dma_active")); r["dma_idle"] = _int(r.get("dma_idle")) + r["dram_responses"] = _int(r.get("dram_responses")); r["read_latency"] = _float(r.get("read_latency")) + +def grp(pred): + g = [r for r in rows if pred(r)] + c = [r["c"] for r in g] + if not g: + return dict(n=0, cmin=None, cmed=None, cmax=None, cmean=None, sa=None, vpu=None, dram=None, reads=None) + return dict(n=len(g), cmin=min(c), cmed=int(st.median(c)), cmax=max(c), cmean=int(st.mean(c)), + sa=round(st.mean(r["sa_avg"] for r in g),1), vpu=round(st.mean(r["vpu"] for r in g),1), + dram=round(st.mean(r["dram_util"] for r in g),1), + reads=int(st.mean(r["dram_responses"] for r in g if r["dram_responses"] is not None))) + +groups = { + "MHA prefill": grp(lambda r: r["mode"]=="prefill" and r["gqa"]=="0"), + "MHA decode": grp(lambda r: r["mode"]=="decode" and r["gqa"]=="0"), + "GQA prefill": grp(lambda r: r["mode"]=="prefill" and r["gqa"]=="1"), + "GQA decode": grp(lambda r: r["mode"]=="decode" and r["gqa"]=="1"), + "ALL": grp(lambda r: True), +} + +# H1 bottleneck classification per case +def bottleneck(r): + d = {"SA (MXU)": r["sa_avg"], "VPU (softmax)": r["vpu"], "DRAM": r["dram_util"]} + return max(d, key=d.get) +bott = {} +for r in rows: + bott[bottleneck(r)] = bott.get(bottleneck(r), 0) + 1 + +# H2a per-head cycles MHA vs GQA (matched B,S,D) +def find(mode,gqa,B,Hq,Hkv,S,D): + for r in rows: + if r["mode"]==mode and r["gqa"]==str(gqa) and r["B"]==B and r["Hq"]==Hq and r["Hkv"]==Hkv and r["S"]==S and r["D"]==D: + return r + return None +perhead = [] +for (B,S,D) in [(1,256,128),(1,512,128),(4,512,128)]: + entry = {"cfg": f"B{B} S{S} D{D}"} + for lbl,(gqa,Hq,Hkv) in {"MHA H8":(0,8,8),"GQA Hq8":(1,8,1),"GQA Hq16":(1,16,1),"GQA Hq64":(1,64,8)}.items(): + r = find("prefill",gqa,B,Hq,Hkv,S,D) + entry[lbl] = round(r["c"]/r["Hq"],0) if r else None + perhead.append(entry) + +# H2b: KV DMA reads & cycles, MHA (ratio1) vs GQA Hq8/Hkv1 (ratio8) at matched Hq=8 +def ratio_pts(mode): + out=[] + for (B,S,D) in [(1,256,128),(1,512,128)]: + pts=[] + for lbl,(gqa,Hq,Hkv,ratio) in {"MHA (ratio1)":(0,8,8,1),"GQA (ratio8)":(1,8,1,8)}.items(): + r = find(mode,gqa,B,Hq,Hkv,S,D) + if r: pts.append({"lbl":lbl,"ratio":ratio,"reads":r["dram_responses"],"cycles":r["c"], + "percyc":round(r["c"]/r["Hq"],0),"dram_util":round(r["dram_util"],1)}) + if len(pts)==2: out.append({"cfg":f"B{B} S{S} D{D}","pts":pts}) + return out +decode_ratio = ratio_pts("decode") +prefill_ratio = ratio_pts("prefill") + +# Scaling vs S (MHA only), with utilization -- prefill capped, decode full long-context range +def scaling_series(mode): + Ss = sorted({r["S"] for r in rows if r["mode"]==mode and r["gqa"]=="0"}) + out=[] + for S in Ss: + g=[r for r in rows if r["mode"]==mode and r["gqa"]=="0" and r["S"]==S] + if not g: continue + rd=[r["dram_responses"] for r in g if r["dram_responses"] is not None] + out.append({"S":S, "cyc":int(st.median(r["c"] for r in g)), + "reads":int(st.median(rd)) if rd else None, + "mxu":round(st.mean(r["sa_avg"] for r in g),1), + "vpu":round(st.mean(r["vpu"] for r in g),1), + "dram":round(st.mean(r["dram_util"] for r in g),1)}) + return out +scaling = {"prefill": scaling_series("prefill"), "decode": scaling_series("decode")} + +data = dict(groups=groups, bottleneck=bott, perhead=perhead, decode_ratio=decode_ratio, + prefill_ratio=prefill_ratio, scaling=scaling, n=len(rows), peak_bw=PEAK_BW) +json.dump(data, open(JOUT,"w"), indent=2) +print("source CSV :", SRC) +print("report JSON :", JOUT) +print(json.dumps(data, indent=2)) diff --git a/scripts/join_sdpa_v6e.py b/scripts/join_sdpa_v6e.py new file mode 100644 index 00000000..3b1a776d --- /dev/null +++ b/scripts/join_sdpa_v6e.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Join real TPU v6e measurements with TOGSim (gem5) simulation results. + +Inputs (under sdpa_results/): + v6e_tpu_measure.csv - real TPU profiler sweep (real_*) + v6e_gem5_measure_enriched.csv - TOGSim simulation sweep (sim_*) + +Output: + v6e_validation_join.csv - inner join on the shape keys, with + sim/real ratio and abs percent error. + +Only the shapes present in BOTH sweeps are emitted. Rows are sorted by +abs_pct_err descending (worst simulator mismatch first). +""" +import csv +import os + +BASE = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +RESULTS = os.path.join(BASE, "sdpa_results") +TPU = os.path.join(RESULTS, "v6e_tpu_measure.csv") +SIM = os.path.join(RESULTS, "v6e_gem5_measure_enriched.csv") +OUT = os.path.join(RESULTS, "v6e_validation_join.csv") + +# Shape identity shared by both sweeps. +KEYS = ["mode", "gqa", "B", "Hq", "Hkv", "S", "D"] + +OUT_COLS = KEYS + [ + "real_cycles", "sim_cycles", "ratio_sim_real", "abs_pct_err", + "real_mxu", "sim_sa_max", "real_hbm_gbs", "sim_dram_gbs", + "real_vpu", "sim_vpu", +] + + +def load(path): + with open(path, newline="") as f: + return list(csv.DictReader(f)) + + +def key(row): + return tuple(row[k] for k in KEYS) + + +def fnum(s): + """Parse a float, treating blank cells as missing (None).""" + s = (s or "").strip() + if not s: + return None + return float(s) + + +def main(): + tpu = load(TPU) + sim = {key(r): r for r in load(SIM)} + + rows = [] + for r in tpu: + s = sim.get(key(r)) + if s is None: + continue # inner join: shape must exist in the sim sweep too + + real_cycles = fnum(r["total_cycles"]) + sim_cycles = fnum(s["total_cycles"]) + if real_cycles is None or sim_cycles is None or real_cycles == 0: + continue + + ratio = sim_cycles / real_cycles + abs_pct_err = abs(sim_cycles - real_cycles) / real_cycles * 100.0 + + sa0 = fnum(s.get("sa0_util")) + sa1 = fnum(s.get("sa1_util")) + sa_vals = [v for v in (sa0, sa1) if v is not None] + sim_sa_max = max(sa_vals) if sa_vals else None + + def g(v): + return "" if v is None else v + + rows.append({ + **{k: r[k] for k in KEYS}, + "real_cycles": int(real_cycles), + "sim_cycles": int(sim_cycles), + "ratio_sim_real": round(ratio, 3), + "abs_pct_err": round(abs_pct_err, 1), + "real_mxu": g(fnum(r.get("mxu_util"))), + "sim_sa_max": g(sim_sa_max), + "real_hbm_gbs": g(fnum(r.get("hbm_bw_gbs"))), + "sim_dram_gbs": g(fnum(s.get("dram_bw_gbs"))), + "real_vpu": g(fnum(r.get("vpu_util"))), + "sim_vpu": g(fnum(s.get("vpu_util"))), + }) + + rows.sort(key=lambda x: x["abs_pct_err"], reverse=True) + + with open(OUT, "w", newline="") as f: + w = csv.DictWriter(f, fieldnames=OUT_COLS) + w.writeheader() + w.writerows(rows) + + print(f"[join] {len(rows)} shapes matched -> {OUT}") + + +if __name__ == "__main__": + main() diff --git a/scripts/measure_sdpa_v6e.py b/scripts/measure_sdpa_v6e.py new file mode 100644 index 00000000..738e24d2 --- /dev/null +++ b/scripts/measure_sdpa_v6e.py @@ -0,0 +1,125 @@ +"""Performance measurement harness for flash-SDPA on the TPU v6e schema. + +Runs the same shape sweep as tests/ops/attention/test_sdpa.py but with Spike +(functional validation) OFF -- only Gem5 (compute-latency tables) + TOGSim +(cycle-accurate) run. For each case it parses the TOGSim log once and writes a +fully enriched row (cycles, SA/VPU utilization, DRAM bandwidth AND the per-case +DMA metrics) directly, so no separate analyze/enrich pass over a stale +intermediate CSV is needed -- the CSV this emits is already the enriched schema. + +Usage: + export TORCHSIM_DIR=/workspace/PyTorchSim + export TOGSIM_CONFIG=/workspace/PyTorchSim/configs/systolic_ws_256x256_c1_simple_noc_tpuv6e_measure.yml + python scripts/measure_sdpa_v6e.py [csv_path] +""" +import os, re, sys, csv, glob, time + +BASE = os.environ.get("TORCHSIM_DIR", "/workspace/PyTorchSim") +sys.path.insert(0, BASE) +sys.path.insert(0, os.path.join(BASE, "tests/ops/attention")) +import torch +import test_sdpa as t + +LOG_DIR = os.path.join(BASE, "togsim_results") +CSV = sys.argv[1] if len(sys.argv) > 1 else os.path.join(BASE, "sdpa_results", "v6e_gem5_measure_enriched.csv") +os.makedirs(os.path.dirname(CSV), exist_ok=True) + +PEAK_BW = 1638.4 # GB/s (HBM3 32ch), for dram_util_pct + +# Enriched schema, emitted directly (superset of the old raw schema). The +# trailing `log` column is kept for traceability; downstream readers use named +# columns so the extra field is harmless. +FIELDS = ["mode", "gqa", "B", "Hq", "Hkv", "S", "D", "total_cycles", + "sa0_util", "sa1_util", "vpu_util", "dram_bw_gbs", "dram_util_pct", + "dma_active", "dma_idle", "dram_responses", "read_latency", "log"] + +_num = lambda m: float(m.group(1)) if m else None + +def parse_latest_log(before_ts): + logs = [p for p in glob.glob(os.path.join(LOG_DIR, "*.log")) if os.path.getmtime(p) >= before_ts] + if not logs: + return {} + path = max(logs, key=os.path.getmtime) + txt = open(path, "r", errors="ignore").read() + sa = re.findall(r"Systolic array \[(\d)\] utilization\(%\):\s*([\d.]+)", txt) + sa_util = {int(i): float(v) for i, v in sa} + # Final cumulative Core DMA summary (last occurrence): active/idle cycles, + # DRAM bandwidth and response count in one line, plus avg read latency. + dma = re.findall(r"DMA active_cycles:\s*(\d+),\s*DMA idle_cycles:\s*(\d+),\s*DRAM BW:\s*([\d.]+) GB/s \((\d+) responses\)", txt) + rl = re.findall(r"avg_read_latency:\s*([\d.]+)", txt) + dma_active = dma_idle = dram_bw = dram_responses = read_latency = None + if dma: + a, i, bw, resp = dma[-1] + dma_active, dma_idle, dram_bw, dram_responses = int(a), int(i), float(bw), int(resp) + read_latency = float(rl[-1]) if rl else None + if dram_bw is None: # fall back to the standalone BW line if the combined one is absent + dram_bw = _num(re.search(r"DMA active_cycles.*?DRAM BW:\s*([\d.]+) GB/s", txt)) + dram_util_pct = round(100.0 * dram_bw / PEAK_BW, 2) if dram_bw is not None else None + return { + "total_cycles": int(m.group(1)) if (m := re.search(r"Total execution cycles:\s*(\d+)", txt)) else None, + "sa0_util": sa_util.get(0), + "sa1_util": sa_util.get(1), + "vpu_util": _num(re.search(r"Vector unit utilization\(%\):\s*([\d.]+)", txt)), + "dram_bw_gbs": dram_bw, + "dram_util_pct": dram_util_pct, + "dma_active": dma_active, + "dma_idle": dma_idle, + "dram_responses": dram_responses, + "read_latency": read_latency, + "log": os.path.basename(path), + } + +def run_case(writer, fh, mode, gqa, B, Hq, Hkv, S, D): + L = 1 if mode == "decode" else S + q = torch.rand(B, Hq, L, D, dtype=torch.float16) + k = torch.rand(B, Hkv, S, D, dtype=torch.float16) + v = torch.rand(B, Hkv, S, D, dtype=torch.float16) + kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=False) + if gqa: + kwargs["enable_gqa"] = True + t.clear_caches() + ts = time.time() + try: + t._run_sdpa(t.device, q, k, v, **kwargs).cpu() + stats = parse_latest_log(ts) + status = "ok" + except Exception as e: + stats = {} + status = f"ERROR:{type(e).__name__}" + row = {"mode": mode, "gqa": int(gqa), "B": B, "Hq": Hq, "Hkv": Hkv, "S": S, "D": D, **stats} + writer.writerow(row); fh.flush() + print(f"[{status}] {mode} gqa={int(gqa)} B{B} Hq{Hq} Hkv{Hkv} S{S} D{D} -> " + f"cycles={stats.get('total_cycles')} sa0={stats.get('sa0_util')} " + f"sa1={stats.get('sa1_util')} vpu={stats.get('vpu_util')} bw={stats.get('dram_bw_gbs')}", + flush=True) + +def main(): + torch.manual_seed(0) + fh = open(CSV, "w", newline="") + writer = csv.DictWriter(fh, fieldnames=FIELDS) + writer.writeheader() + n = 0 + with torch.nn.attention.sdpa_kernel([torch.nn.attention.SDPBackend.FLASH_ATTENTION]): + # MHA prefill + decode. Prefill is O(S^2) so cap it; decode (L=1) is cheap + # and stays on the full S range for long-context KV-cache scaling. + PREFILL_S_CAP = 2048 + for mode in ("prefill", "decode"): + seqs = t.SEQ_LIST if mode == "decode" else [s for s in t.SEQ_LIST if s <= PREFILL_S_CAP] + for B in t.BATCH_LIST: + for H in t.HEAD_LIST: + for S in seqs: + for D in t.HEAD_DIM_LIST: + run_case(writer, fh, mode, False, B, H, H, S, D); n += 1 + # GQA prefill + decode + for mode in ("prefill", "decode"): + for B in t.BATCH_LIST: + for Hkv, ratio in t.GQA_HEAD_CONFIGS: + Hq = ratio * Hkv + for S in t.GQA_SEQ_LIST: + for D in t.GQA_HEAD_DIM_LIST: + run_case(writer, fh, mode, True, B, Hq, Hkv, S, D); n += 1 + fh.close() + print(f"DONE: {n} cases measured -> {CSV}", flush=True) + +if __name__ == "__main__": + main() diff --git a/sdpa_results/v6e_tpu_measure.csv b/sdpa_results/v6e_tpu_measure.csv new file mode 100644 index 00000000..099628bf --- /dev/null +++ b/sdpa_results/v6e_tpu_measure.csv @@ -0,0 +1,559 @@ +mode,gqa,B,Hq,Hkv,S,D,d_pad,total_cycles,mxu_util,vpu_util,hbm_bw_gbs,device_us,avg_us,util_src,flops,bytes,fused,trace_dir,error +prefill,0,1,1,1,128,128,0,6280,0.51,,73.1,1.7933,46.841,analytic,8388608,131072,True,tb_logs/prefill_gqa0_B1_Hq1_Hkv1_S128_D128, +prefill,0,1,1,1,256,128,0,7157,1.79,,128.3,2.0436,43.878,analytic,33554432,262144,True,tb_logs/prefill_gqa0_B1_Hq1_Hkv1_S256_D128, +prefill,0,1,1,1,512,128,0,8914,5.74,,206.0,2.5455,50.413,analytic,134217728,524288,True,tb_logs/prefill_gqa0_B1_Hq1_Hkv1_S512_D128, +prefill,0,1,1,1,1024,128,0,17844,11.48,,205.8,5.0954,49.69,analytic,536870912,1048576,True,tb_logs/prefill_gqa0_B1_Hq1_Hkv1_S1024_D128, +prefill,0,1,1,1,2048,128,0,44877,18.26,,163.7,12.8146,37.743,analytic,2147483648,2097152,True,tb_logs/prefill_gqa0_B1_Hq1_Hkv1_S2048_D128, +prefill,0,1,4,4,128,128,0,11231,1.14,,163.5,3.207,43.829,analytic,33554432,524288,True,tb_logs/prefill_gqa0_B1_Hq4_Hkv4_S128_D128, +prefill,0,1,4,4,256,128,0,13136,3.9,,279.5,3.7511,33.69,analytic,134217728,1048576,True,tb_logs/prefill_gqa0_B1_Hq4_Hkv4_S256_D128, +prefill,0,1,4,4,512,128,0,17552,11.67,,418.4,5.012,48.06,analytic,536870912,2097152,True,tb_logs/prefill_gqa0_B1_Hq4_Hkv4_S512_D128, +prefill,0,1,4,4,1024,128,0,48313,16.96,,304.0,13.7959,41.92,analytic,2147483648,4194304,True,tb_logs/prefill_gqa0_B1_Hq4_Hkv4_S1024_D128, +prefill,0,1,4,4,2048,128,0,156718,20.91,,187.5,44.7511,59.92,analytic,8589934592,8388608,True,tb_logs/prefill_gqa0_B1_Hq4_Hkv4_S2048_D128, +prefill,0,1,8,8,128,128,0,17728,1.44,,207.1,5.0623,48.055,analytic,67108864,1048576,True,tb_logs/prefill_gqa0_B1_Hq8_Hkv8_S128_D128, +prefill,0,1,8,8,256,128,0,21086,4.86,,348.3,6.0212,47.757,analytic,268435456,2097152,True,tb_logs/prefill_gqa0_B1_Hq8_Hkv8_S256_D128, +prefill,0,1,8,8,512,128,0,28483,14.38,,515.7,8.1332,50.241,analytic,1073741824,4194304,True,tb_logs/prefill_gqa0_B1_Hq8_Hkv8_S512_D128, +prefill,0,1,8,8,1024,128,0,89216,18.36,,329.3,25.4757,52.44,analytic,4294967296,8388608,True,tb_logs/prefill_gqa0_B1_Hq8_Hkv8_S1024_D128, +prefill,0,1,8,8,2048,128,0,305512,21.45,,192.3,87.2394,113.364,analytic,17179869184,16777216,True,tb_logs/prefill_gqa0_B1_Hq8_Hkv8_S2048_D128, +prefill,0,2,1,1,128,128,0,8100,0.79,,113.3,2.3128,39.895,analytic,16777216,262144,True,tb_logs/prefill_gqa0_B2_Hq1_Hkv1_S128_D128, +prefill,0,2,1,1,256,128,0,9353,2.74,,196.3,2.6707,39.911,analytic,67108864,524288,True,tb_logs/prefill_gqa0_B2_Hq1_Hkv1_S256_D128, +prefill,0,2,1,1,512,128,0,11909,8.6,,308.3,3.4007,44.544,analytic,268435456,1048576,True,tb_logs/prefill_gqa0_B2_Hq1_Hkv1_S512_D128, +prefill,0,2,1,1,1024,128,0,29827,13.73,,246.2,8.5172,47.154,analytic,1073741824,2097152,True,tb_logs/prefill_gqa0_B2_Hq1_Hkv1_S1024_D128, +prefill,0,2,1,1,2048,128,0,86976,18.84,,168.9,24.836,45.97,analytic,4294967296,4194304,True,tb_logs/prefill_gqa0_B2_Hq1_Hkv1_S2048_D128, +prefill,0,2,4,4,128,128,0,18497,1.38,,198.5,5.282,33.624,analytic,67108864,1048576,True,tb_logs/prefill_gqa0_B2_Hq4_Hkv4_S128_D128, +prefill,0,2,4,4,256,128,0,22132,4.63,,331.8,6.3197,33.804,analytic,268435456,2097152,True,tb_logs/prefill_gqa0_B2_Hq4_Hkv4_S256_D128, +prefill,0,2,4,4,512,128,0,29871,13.71,,491.7,8.5297,33.412,analytic,1073741824,4194304,True,tb_logs/prefill_gqa0_B2_Hq4_Hkv4_S512_D128, +prefill,0,2,4,4,1024,128,0,93475,17.53,,314.3,26.6918,35.973,analytic,4294967296,8388608,True,tb_logs/prefill_gqa0_B2_Hq4_Hkv4_S1024_D128, +prefill,0,2,4,4,2048,128,0,322384,20.33,,182.2,92.0571,114.705,analytic,17179869184,16777216,True,tb_logs/prefill_gqa0_B2_Hq4_Hkv4_S2048_D128, +prefill,0,2,8,8,128,128,0,32005,1.6,,229.5,9.139,34.753,analytic,134217728,2097152,True,tb_logs/prefill_gqa0_B2_Hq8_Hkv8_S128_D128, +prefill,0,2,8,8,256,128,0,38397,5.33,,382.5,10.9644,33.68,analytic,536870912,4194304,True,tb_logs/prefill_gqa0_B2_Hq8_Hkv8_S256_D128, +prefill,0,2,8,8,512,128,0,52926,15.48,,555.1,15.1131,33.499,analytic,2147483648,8388608,True,tb_logs/prefill_gqa0_B2_Hq8_Hkv8_S512_D128, +prefill,0,2,8,8,1024,128,0,178495,18.36,,329.2,50.9695,64.747,analytic,8589934592,16777216,True,tb_logs/prefill_gqa0_B2_Hq8_Hkv8_S1024_D128, +prefill,0,2,8,8,2048,128,0,636189,20.6,,184.7,181.6644,231.417,analytic,34359738368,33554432,True,tb_logs/prefill_gqa0_B2_Hq8_Hkv8_S2048_D128, +prefill,0,4,1,1,128,128,0,11594,1.1,,158.4,3.3106,34.585,analytic,33554432,524288,True,tb_logs/prefill_gqa0_B4_Hq1_Hkv1_S128_D128, +prefill,0,4,1,1,256,128,0,13858,3.69,,265.0,3.9572,33.706,analytic,134217728,1048576,True,tb_logs/prefill_gqa0_B4_Hq1_Hkv1_S256_D128, +prefill,0,4,1,1,512,128,0,18151,11.28,,404.6,5.183,33.915,analytic,536870912,2097152,True,tb_logs/prefill_gqa0_B4_Hq1_Hkv1_S512_D128, +prefill,0,4,1,1,1024,128,0,52573,15.58,,279.4,15.0122,33.445,analytic,2147483648,4194304,True,tb_logs/prefill_gqa0_B4_Hq1_Hkv1_S1024_D128, +prefill,0,4,1,1,2048,128,0,166145,19.72,,176.8,47.4428,61.187,analytic,8589934592,8388608,True,tb_logs/prefill_gqa0_B4_Hq1_Hkv1_S2048_D128, +prefill,0,4,4,4,128,128,0,32560,1.57,,225.6,9.2977,33.751,analytic,134217728,2097152,True,tb_logs/prefill_gqa0_B4_Hq4_Hkv4_S128_D128, +prefill,0,4,4,4,256,128,0,39203,5.22,,374.7,11.1944,33.72,analytic,536870912,4194304,True,tb_logs/prefill_gqa0_B4_Hq4_Hkv4_S256_D128, +prefill,0,4,4,4,512,128,0,54243,15.1,,541.6,15.4893,33.784,analytic,2147483648,8388608,True,tb_logs/prefill_gqa0_B4_Hq4_Hkv4_S512_D128, +prefill,0,4,4,4,1024,128,0,180063,18.2,,326.3,51.4171,65.616,analytic,8589934592,16777216,True,tb_logs/prefill_gqa0_B4_Hq4_Hkv4_S1024_D128, +prefill,0,4,4,4,2048,128,0,637850,20.55,,184.2,182.1388,225.522,analytic,34359738368,33554432,True,tb_logs/prefill_gqa0_B4_Hq4_Hkv4_S2048_D128, +prefill,0,4,8,8,128,128,0,59423,1.72,,247.2,16.9682,41.658,analytic,268435456,4194304,True,tb_logs/prefill_gqa0_B4_Hq8_Hkv8_S128_D128, +prefill,0,4,8,8,256,128,0,71702,5.71,,409.7,20.4747,48.327,analytic,1073741824,8388608,True,tb_logs/prefill_gqa0_B4_Hq8_Hkv8_S256_D128, +prefill,0,4,8,8,512,128,0,100330,16.33,,585.6,28.6494,48.73,analytic,4294967296,16777216,True,tb_logs/prefill_gqa0_B4_Hq8_Hkv8_S512_D128, +prefill,0,4,8,8,1024,128,0,350134,18.72,,335.6,99.981,125.113,analytic,17179869184,33554432,True,tb_logs/prefill_gqa0_B4_Hq8_Hkv8_S1024_D128, +prefill,0,4,8,8,2048,128,0,1265220,20.72,,185.8,361.2849,437.637,analytic,68719476736,67108864,True,tb_logs/prefill_gqa0_B4_Hq8_Hkv8_S2048_D128, +prefill,0,8,1,1,128,128,0,18915,1.35,,194.1,5.4013,47.719,analytic,67108864,1048576,True,tb_logs/prefill_gqa0_B8_Hq1_Hkv1_S128_D128, +prefill,0,8,1,1,256,128,0,22832,4.49,,321.7,6.5198,47.828,analytic,268435456,2097152,True,tb_logs/prefill_gqa0_B8_Hq1_Hkv1_S256_D128, +prefill,0,8,1,1,512,128,0,30937,13.24,,474.8,8.8341,47.634,analytic,1073741824,4194304,True,tb_logs/prefill_gqa0_B8_Hq1_Hkv1_S512_D128, +prefill,0,8,1,1,1024,128,0,97871,16.74,,300.2,27.9473,42.743,analytic,4294967296,8388608,True,tb_logs/prefill_gqa0_B8_Hq1_Hkv1_S1024_D128, +prefill,0,8,1,1,2048,128,0,325336,20.14,,180.6,92.9,117.256,analytic,17179869184,16777216,True,tb_logs/prefill_gqa0_B8_Hq1_Hkv1_S2048_D128, +prefill,0,8,4,4,128,128,0,60340,1.7,,243.4,17.2303,47.744,analytic,268435456,4194304,True,tb_logs/prefill_gqa0_B8_Hq4_Hkv4_S128_D128, +prefill,0,8,4,4,256,128,0,73318,5.59,,400.7,20.9361,47.608,analytic,1073741824,8388608,True,tb_logs/prefill_gqa0_B8_Hq4_Hkv4_S256_D128, +prefill,0,8,4,4,512,128,0,102794,15.94,,571.6,29.3529,48.351,analytic,4294967296,16777216,True,tb_logs/prefill_gqa0_B8_Hq4_Hkv4_S512_D128, +prefill,0,8,4,4,1024,128,0,353357,18.55,,332.5,100.9015,126.363,analytic,17179869184,33554432,True,tb_logs/prefill_gqa0_B8_Hq4_Hkv4_S1024_D128, +prefill,0,8,4,4,2048,128,0,1268284,20.67,,185.3,362.1599,439.851,analytic,68719476736,67108864,True,tb_logs/prefill_gqa0_B8_Hq4_Hkv4_S2048_D128, +prefill,0,8,8,8,128,128,0,113853,1.8,,258.0,32.5108,48.629,analytic,536870912,8388608,True,tb_logs/prefill_gqa0_B8_Hq8_Hkv8_S128_D128, +prefill,0,8,8,8,256,128,0,138525,5.91,,424.1,39.556,56.689,analytic,2147483648,16777216,True,tb_logs/prefill_gqa0_B8_Hq8_Hkv8_S256_D128, +prefill,0,8,8,8,512,128,0,195310,16.78,,601.6,55.771,73.54,analytic,8589934592,33554432,True,tb_logs/prefill_gqa0_B8_Hq8_Hkv8_S512_D128, +prefill,0,8,8,8,1024,128,0,692930,18.92,,339.2,197.8669,254.849,analytic,34359738368,67108864,True,tb_logs/prefill_gqa0_B8_Hq8_Hkv8_S1024_D128, +prefill,0,8,8,8,2048,128,0,2709045,19.35,,173.5,773.5707,874.039,analytic,137438953472,134217728,True,tb_logs/prefill_gqa0_B8_Hq8_Hkv8_S2048_D128, +prefill,0,16,1,1,128,128,0,33307,1.54,,220.5,9.5107,47.92,analytic,134217728,2097152,True,tb_logs/prefill_gqa0_B16_Hq1_Hkv1_S128_D128, +prefill,0,16,1,1,256,128,0,40615,5.04,,361.6,11.5977,47.92,analytic,536870912,4194304,True,tb_logs/prefill_gqa0_B16_Hq1_Hkv1_S256_D128, +prefill,0,16,1,1,512,128,0,56087,14.61,,523.8,16.0156,36.344,analytic,2147483648,8388608,True,tb_logs/prefill_gqa0_B16_Hq1_Hkv1_S512_D128, +prefill,0,16,1,1,1024,128,0,189318,17.31,,310.3,54.06,72.792,analytic,8589934592,16777216,True,tb_logs/prefill_gqa0_B16_Hq1_Hkv1_S1024_D128, +prefill,0,16,1,1,2048,128,0,644070,20.35,,182.4,183.9149,230.826,analytic,34359738368,33554432,True,tb_logs/prefill_gqa0_B16_Hq1_Hkv1_S2048_D128, +prefill,0,16,4,4,128,128,0,115970,1.77,,253.3,33.1154,49.654,analytic,536870912,8388608,True,tb_logs/prefill_gqa0_B16_Hq4_Hkv4_S128_D128, +prefill,0,16,4,4,256,128,0,141959,5.77,,413.9,40.5365,59.075,analytic,2147483648,16777216,True,tb_logs/prefill_gqa0_B16_Hq4_Hkv4_S256_D128, +prefill,0,16,4,4,512,128,0,200195,16.37,,587.0,57.1659,77.631,analytic,8589934592,33554432,True,tb_logs/prefill_gqa0_B16_Hq4_Hkv4_S512_D128, +prefill,0,16,4,4,1024,128,0,699288,18.74,,336.1,199.6826,243.816,analytic,34359738368,67108864,True,tb_logs/prefill_gqa0_B16_Hq4_Hkv4_S1024_D128, +prefill,0,16,4,4,2048,128,0,2719629,19.28,,172.8,776.5931,877.558,analytic,137438953472,134217728,True,tb_logs/prefill_gqa0_B16_Hq4_Hkv4_S2048_D128, +prefill,0,16,8,8,128,128,0,223220,1.84,,263.2,63.7406,87.841,analytic,1073741824,16777216,True,tb_logs/prefill_gqa0_B16_Hq8_Hkv8_S128_D128, +prefill,0,16,8,8,256,128,0,272275,6.02,,431.6,77.7486,106.053,analytic,4294967296,33554432,True,tb_logs/prefill_gqa0_B16_Hq8_Hkv8_S256_D128, +prefill,0,16,8,8,512,128,0,384580,17.04,,611.1,109.8173,141.08,analytic,17179869184,67108864,True,tb_logs/prefill_gqa0_B16_Hq8_Hkv8_S512_D128, +prefill,0,16,8,8,1024,128,0,1441140,18.19,,326.2,411.519,477.919,analytic,68719476736,134217728,True,tb_logs/prefill_gqa0_B16_Hq8_Hkv8_S1024_D128, +prefill,0,16,8,8,2048,128,0,6232817,16.82,,150.8,1779.7879,1729.826,analytic,274877906944,268435456,True,tb_logs/prefill_gqa0_B16_Hq8_Hkv8_S2048_D128, +prefill,0,32,1,1,128,128,0,61831,1.66,,237.6,17.656,48.401,analytic,268435456,4194304,True,tb_logs/prefill_gqa0_B32_Hq1_Hkv1_S128_D128, +prefill,0,32,1,1,256,128,0,75984,5.39,,386.6,21.6974,48.621,analytic,1073741824,8388608,True,tb_logs/prefill_gqa0_B32_Hq1_Hkv1_S256_D128, +prefill,0,32,1,1,512,128,0,106959,15.32,,549.3,30.5421,49.849,analytic,4294967296,16777216,True,tb_logs/prefill_gqa0_B32_Hq1_Hkv1_S512_D128, +prefill,0,32,1,1,1024,128,0,371298,17.65,,316.5,106.0246,138.108,analytic,17179869184,33554432,True,tb_logs/prefill_gqa0_B32_Hq1_Hkv1_S1024_D128, +prefill,0,32,1,1,2048,128,0,1280357,20.47,,183.6,365.6073,449.575,analytic,68719476736,67108864,True,tb_logs/prefill_gqa0_B32_Hq1_Hkv1_S2048_D128, +prefill,0,32,4,4,128,128,0,227254,1.8,,258.5,64.8927,90.545,analytic,1073741824,16777216,True,tb_logs/prefill_gqa0_B32_Hq4_Hkv4_S128_D128, +prefill,0,32,4,4,256,128,0,279077,5.87,,421.1,79.6907,111.191,analytic,4294967296,33554432,True,tb_logs/prefill_gqa0_B32_Hq4_Hkv4_S256_D128, +prefill,0,32,4,4,512,128,0,394507,16.61,,595.7,112.652,148.947,analytic,17179869184,67108864,True,tb_logs/prefill_gqa0_B32_Hq4_Hkv4_S512_D128, +prefill,0,32,4,4,1024,128,0,1465315,17.89,,320.8,418.4222,488.842,analytic,68719476736,134217728,True,tb_logs/prefill_gqa0_B32_Hq4_Hkv4_S1024_D128, +prefill,0,32,4,4,2048,128,0,6290788,16.67,,149.4,1796.3416,1745.103,analytic,274877906944,268435456,True,tb_logs/prefill_gqa0_B32_Hq4_Hkv4_S2048_D128, +prefill,0,32,8,8,128,128,0,441683,1.85,,266.0,126.1232,171.84,analytic,2147483648,33554432,True,tb_logs/prefill_gqa0_B32_Hq8_Hkv8_S128_D128, +prefill,0,32,8,8,256,128,0,539620,6.07,,435.5,154.0891,208.182,analytic,8589934592,67108864,True,tb_logs/prefill_gqa0_B32_Hq8_Hkv8_S256_D128, +prefill,0,32,8,8,512,128,0,792094,16.55,,593.4,226.1834,272.298,analytic,34359738368,134217728,True,tb_logs/prefill_gqa0_B32_Hq8_Hkv8_S512_D128, +prefill,0,32,8,8,1024,128,0,3501788,14.97,,268.5,999.9396,948.505,analytic,137438953472,268435456,True,tb_logs/prefill_gqa0_B32_Hq8_Hkv8_S1024_D128, +prefill,0,32,8,8,2048,128,0,12766610,16.43,,147.3,3645.5196,3463.732,analytic,549755813888,536870912,True,tb_logs/prefill_gqa0_B32_Hq8_Hkv8_S2048_D128, +decode,0,1,1,1,128,128,0,5690,0.0,,40.6,1.6249,32.32,analytic,65536,66048,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S128_D128, +decode,0,1,1,1,256,128,0,6039,0.01,,76.3,1.7244,31.578,analytic,131072,131584,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S256_D128, +decode,0,1,1,1,512,128,0,6399,0.02,,143.7,1.8273,31.866,analytic,262144,262656,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S512_D128, +decode,0,1,1,1,1024,128,0,9621,0.02,,191.0,2.7472,31.701,analytic,524288,524800,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S1024_D128, +decode,0,1,1,1,2048,128,0,14130,0.03,,260.0,4.0349,47.766,analytic,1048576,1049088,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S2048_D128, +decode,0,1,1,1,4096,128,0,22852,0.04,,321.5,6.5254,47.101,analytic,2097152,2097664,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S4096_D128, +decode,0,1,1,1,8192,128,0,39991,0.04,,367.3,11.4194,47.305,analytic,4194304,4194816,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S8192_D128, +decode,0,1,1,1,16384,128,0,74154,0.04,,396.2,21.1747,46.971,analytic,8388608,8389120,True,tb_logs/decode_gqa0_B1_Hq1_Hkv1_S16384_D128, +decode,0,1,4,4,128,128,0,10996,0.01,,84.1,3.1399,47.79,analytic,262144,264192,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S128_D128, +decode,0,1,4,4,256,128,0,11724,0.02,,157.2,3.3477,47.903,analytic,524288,526336,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S256_D128, +decode,0,1,4,4,512,128,0,12788,0.03,,287.7,3.6515,47.514,analytic,1048576,1050624,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S512_D128, +decode,0,1,4,4,1024,128,0,22832,0.04,,322.0,6.5197,47.31,analytic,2097152,2099200,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S1024_D128, +decode,0,1,4,4,2048,128,0,39950,0.04,,367.9,11.4076,47.515,analytic,4194304,4196352,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S2048_D128, +decode,0,1,4,4,4096,128,0,74325,0.04,,395.3,21.2237,47.397,analytic,8388608,8390656,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S4096_D128, +decode,0,1,4,4,8192,128,0,142748,0.04,,411.6,40.7617,48.176,analytic,16777216,16779264,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S8192_D128, +decode,0,1,4,4,16384,128,0,277995,0.05,,422.7,79.3818,84.334,analytic,33554432,33556480,True,tb_logs/decode_gqa0_B1_Hq4_Hkv4_S16384_D128, +decode,0,1,8,8,128,128,0,18518,0.01,,99.9,5.2879,47.811,analytic,524288,528384,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S128_D128, +decode,0,1,8,8,256,128,0,19776,0.02,,186.4,5.6472,47.636,analytic,1048576,1052672,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S256_D128, +decode,0,1,8,8,512,128,0,21703,0.04,,339.1,6.1974,47.745,analytic,2097152,2101248,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S512_D128, +decode,0,1,8,8,1024,128,0,40377,0.04,,364.1,11.5297,47.784,analytic,4194304,4198400,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S1024_D128, +decode,0,1,8,8,2048,128,0,75011,0.04,,391.8,21.4195,47.471,analytic,8388608,8392704,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S2048_D128, +decode,0,1,8,8,4096,128,0,143580,0.04,,409.3,40.9994,48.184,analytic,16777216,16781312,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S4096_D128, +decode,0,1,8,8,8192,128,0,278135,0.05,,422.5,79.4219,84.414,analytic,33554432,33558528,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S8192_D128, +decode,0,1,8,8,16384,128,0,555448,0.05,,423.1,158.6088,163.751,analytic,67108864,67112960,True,tb_logs/decode_gqa0_B1_Hq8_Hkv8_S16384_D128, +decode,0,2,1,1,128,128,0,7470,0.01,,61.9,2.133,47.137,analytic,131072,132096,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S128_D128, +decode,0,2,1,1,256,128,0,7836,0.01,,117.6,2.2375,47.691,analytic,262144,263168,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S256_D128, +decode,0,2,1,1,512,128,0,8420,0.02,,218.5,2.4043,47.172,analytic,524288,525312,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S512_D128, +decode,0,2,1,1,1024,128,0,14278,0.03,,257.4,4.0772,47.348,analytic,1048576,1049600,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S1024_D128, +decode,0,2,1,1,2048,128,0,22756,0.04,,322.9,6.4981,47.176,analytic,2097152,2098176,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S2048_D128, +decode,0,2,1,1,4096,128,0,40061,0.04,,366.7,11.4394,47.463,analytic,4194304,4195328,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S4096_D128, +decode,0,2,1,1,8192,128,0,73992,0.04,,397.1,21.1284,47.481,analytic,8388608,8389632,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S8192_D128, +decode,0,2,1,1,16384,128,0,142969,0.04,,411.0,40.8251,47.222,analytic,16777216,16778240,True,tb_logs/decode_gqa0_B2_Hq1_Hkv1_S16384_D128, +decode,0,2,4,4,128,128,0,18515,0.01,,99.9,5.2869,46.601,analytic,524288,528384,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S128_D128, +decode,0,2,4,4,256,128,0,19661,0.02,,187.5,5.6141,46.522,analytic,1048576,1052672,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S256_D128, +decode,0,2,4,4,512,128,0,21877,0.04,,336.4,6.247,46.548,analytic,2097152,2101248,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S512_D128, +decode,0,2,4,4,1024,128,0,41379,0.04,,355.3,11.8157,47.123,analytic,4194304,4198400,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S1024_D128, +decode,0,2,4,4,2048,128,0,75545,0.04,,389.1,21.5721,47.009,analytic,8388608,8392704,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S2048_D128, +decode,0,2,4,4,4096,128,0,144133,0.04,,407.7,41.1574,47.811,analytic,16777216,16781312,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S4096_D128, +decode,0,2,4,4,8192,128,0,279352,0.05,,420.7,79.7693,84.9,analytic,33554432,33558528,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S8192_D128, +decode,0,2,4,4,16384,128,0,557940,0.05,,421.2,159.3205,164.4,analytic,67108864,67112960,True,tb_logs/decode_gqa0_B2_Hq4_Hkv4_S16384_D128, +decode,0,2,8,8,128,128,0,33508,0.01,,110.4,9.5683,47.927,analytic,1048576,1056768,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S128_D128, +decode,0,2,8,8,256,128,0,35519,0.02,,207.6,10.1424,47.83,analytic,2097152,2105344,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S256_D128, +decode,0,2,8,8,512,128,0,39642,0.04,,371.2,11.3199,47.819,analytic,4194304,4202496,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S512_D128, +decode,0,2,8,8,1024,128,0,77705,0.04,,378.4,22.1889,48.26,analytic,8388608,8396800,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S1024_D128, +decode,0,2,8,8,2048,128,0,145457,0.04,,404.1,41.5354,47.496,analytic,16777216,16785408,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S2048_D128, +decode,0,2,8,8,4096,128,0,280775,0.05,,418.6,80.1757,85.21,analytic,33554432,33562624,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S4096_D128, +decode,0,2,8,8,8192,128,0,559021,0.05,,420.5,159.629,164.867,analytic,67108864,67117056,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S8192_D128, +decode,0,2,8,8,16384,128,0,1104024,0.05,,425.8,315.2553,320.676,analytic,134217728,134225920,True,tb_logs/decode_gqa0_B2_Hq8_Hkv8_S16384_D128, +decode,0,4,1,1,128,128,0,11264,0.01,,82.1,3.2164,46.531,analytic,262144,264192,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S128_D128, +decode,0,4,1,1,256,128,0,12259,0.02,,150.4,3.5005,47.102,analytic,524288,526336,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S256_D128, +decode,0,4,1,1,512,128,0,13120,0.03,,280.4,3.7465,46.648,analytic,1048576,1050624,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S512_D128, +decode,0,4,1,1,1024,128,0,23365,0.03,,314.6,6.6719,46.638,analytic,2097152,2099200,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S1024_D128, +decode,0,4,1,1,2048,128,0,40362,0.04,,364.1,11.5254,47.149,analytic,4194304,4196352,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S2048_D128, +decode,0,4,1,1,4096,128,0,74977,0.04,,391.9,21.4098,47.523,analytic,8388608,8390656,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S4096_D128, +decode,0,4,1,1,8192,128,0,143789,0.04,,408.7,41.0592,46.93,analytic,16777216,16779264,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S8192_D128, +decode,0,4,1,1,16384,128,0,280725,0.05,,418.6,80.1614,84.676,analytic,33554432,33556480,True,tb_logs/decode_gqa0_B4_Hq1_Hkv1_S16384_D128, +decode,0,4,4,4,128,128,0,33567,0.01,,110.3,9.585,47.947,analytic,1048576,1056768,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S128_D128, +decode,0,4,4,4,256,128,0,35655,0.02,,206.8,10.1813,47.921,analytic,2097152,2105344,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S256_D128, +decode,0,4,4,4,512,128,0,39858,0.04,,369.2,11.3814,47.235,analytic,4194304,4202496,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S512_D128, +decode,0,4,4,4,1024,128,0,77202,0.04,,380.9,22.0451,47.798,analytic,8388608,8396800,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S1024_D128, +decode,0,4,4,4,2048,128,0,146224,0.04,,402.0,41.7545,48.315,analytic,16777216,16785408,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S2048_D128, +decode,0,4,4,4,4096,128,0,282354,0.05,,416.3,80.6266,85.7,analytic,33554432,33562624,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S4096_D128, +decode,0,4,4,4,8192,128,0,557007,0.05,,422.0,159.0539,164.229,analytic,67108864,67117056,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S8192_D128, +decode,0,4,4,4,16384,128,0,1104061,0.05,,425.8,315.2658,320.678,analytic,134217728,134225920,True,tb_logs/decode_gqa0_B4_Hq4_Hkv4_S16384_D128, +decode,0,4,8,8,128,128,0,63412,0.01,,116.7,18.1075,47.402,analytic,2097152,2113536,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S128_D128, +decode,0,4,8,8,256,128,0,66954,0.02,,220.2,19.1188,47.689,analytic,4194304,4210688,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S256_D128, +decode,0,4,8,8,512,128,0,75380,0.04,,390.5,21.525,47.729,analytic,8388608,8404992,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S512_D128, +decode,0,4,8,8,1024,128,0,150274,0.04,,391.4,42.911,48.232,analytic,16777216,16793600,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S1024_D128, +decode,0,4,8,8,2048,128,0,284975,0.04,,412.5,81.3749,86.516,analytic,33554432,33570816,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S2048_D128, +decode,0,4,8,8,4096,128,0,559678,0.05,,420.0,159.8166,164.439,analytic,67108864,67125248,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S4096_D128, +decode,0,4,8,8,8192,128,0,1106160,0.05,,425.0,315.8651,320.704,analytic,134217728,134234112,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S8192_D128, +decode,0,4,8,8,16384,128,0,2205273,0.05,,426.3,629.7181,635.114,analytic,268435456,268451840,True,tb_logs/decode_gqa0_B4_Hq8_Hkv8_S16384_D128, +decode,0,8,1,1,128,128,0,18856,0.01,,98.1,5.3843,31.965,analytic,524288,528384,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S128_D128, +decode,0,8,1,1,256,128,0,20083,0.02,,183.6,5.7348,32.141,analytic,1048576,1052672,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S256_D128, +decode,0,8,1,1,512,128,0,22107,0.04,,332.9,6.3127,32.065,analytic,2097152,2101248,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S512_D128, +decode,0,8,1,1,1024,128,0,41691,0.04,,352.7,11.905,41.544,analytic,4194304,4198400,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S1024_D128, +decode,0,8,1,1,2048,128,0,76041,0.04,,386.5,21.7135,47.105,analytic,8388608,8392704,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S2048_D128, +decode,0,8,1,1,4096,128,0,144732,0.04,,406.0,41.3283,47.839,analytic,16777216,16781312,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S4096_D128, +decode,0,8,1,1,8192,128,0,281476,0.05,,417.5,80.3757,85.371,analytic,33554432,33558528,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S8192_D128, +decode,0,8,1,1,16384,128,0,556188,0.05,,422.6,158.8201,164.057,analytic,67108864,67112960,True,tb_logs/decode_gqa0_B8_Hq1_Hkv1_S16384_D128, +decode,0,8,4,4,128,128,0,63207,0.01,,117.1,18.0488,47.83,analytic,2097152,2113536,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S128_D128, +decode,0,8,4,4,256,128,0,67065,0.02,,219.9,19.1506,47.786,analytic,4194304,4210688,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S256_D128, +decode,0,8,4,4,512,128,0,75468,0.04,,390.0,21.5501,34.907,analytic,8388608,8404992,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S512_D128, +decode,0,8,4,4,1024,128,0,149990,0.04,,392.1,42.8298,47.007,analytic,16777216,16793600,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S1024_D128, +decode,0,8,4,4,2048,128,0,287201,0.04,,409.3,82.0106,86.606,analytic,33554432,33570816,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S2048_D128, +decode,0,8,4,4,4096,128,0,560636,0.05,,419.3,160.0903,165.27,analytic,67108864,67125248,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S4096_D128, +decode,0,8,4,4,8192,128,0,1106629,0.05,,424.8,315.9992,321.65,analytic,134217728,134234112,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S8192_D128, +decode,0,8,4,4,16384,128,0,2205764,0.05,,426.2,629.8584,636.061,analytic,268435456,268451840,True,tb_logs/decode_gqa0_B8_Hq4_Hkv4_S16384_D128, +decode,0,8,8,8,128,128,0,120680,0.01,,122.7,34.4603,39.095,analytic,4194304,4227072,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S128_D128, +decode,0,8,8,8,256,128,0,129228,0.02,,228.2,36.9012,44.057,analytic,8388608,8421376,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S256_D128, +decode,0,8,8,8,512,128,0,146966,0.04,,400.6,41.9662,49.384,analytic,16777216,16809984,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S512_D128, +decode,0,8,8,8,1024,128,0,295192,0.04,,398.5,84.2923,89.002,analytic,33554432,33587200,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S1024_D128, +decode,0,8,8,8,2048,128,0,566428,0.05,,415.1,161.744,167.44,analytic,67108864,67141632,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S2048_D128, +decode,0,8,8,8,4096,128,0,1111826,0.05,,422.9,317.4831,323.084,analytic,134217728,134250496,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S4096_D128, +decode,0,8,8,8,8192,128,0,2210862,0.05,,425.3,631.314,637.168,analytic,268435456,268468224,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S8192_D128, +decode,0,8,8,8,16384,128,0,4384795,0.05,,428.8,1252.083,1258.67,analytic,536870912,536903680,True,tb_logs/decode_gqa0_B8_Hq8_Hkv8_S16384_D128, +decode,0,16,1,1,128,128,0,33920,0.01,,109.1,9.6858,47.501,analytic,1048576,1056768,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S128_D128, +decode,0,16,1,1,256,128,0,35858,0.02,,205.6,10.2394,47.702,analytic,2097152,2105344,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S256_D128, +decode,0,16,1,1,512,128,0,40006,0.04,,367.9,11.4238,47.543,analytic,4194304,4202496,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S512_D128, +decode,0,16,1,1,1024,128,0,78025,0.04,,376.9,22.2801,47.674,analytic,8388608,8396800,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S1024_D128, +decode,0,16,1,1,2048,128,0,146883,0.04,,400.2,41.9427,48.176,analytic,16777216,16785408,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S2048_D128, +decode,0,16,1,1,4096,128,0,283008,0.05,,415.3,80.8134,85.944,analytic,33554432,33562624,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S4096_D128, +decode,0,16,1,1,8192,128,0,557677,0.05,,421.5,159.2452,164.476,analytic,67108864,67117056,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S8192_D128, +decode,0,16,1,1,16384,128,0,1104918,0.05,,425.4,315.5106,320.87,analytic,134217728,134225920,True,tb_logs/decode_gqa0_B16_Hq1_Hkv1_S16384_D128, +decode,0,16,4,4,128,128,0,120754,0.01,,122.6,34.4814,48.926,analytic,4194304,4227072,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S128_D128, +decode,0,16,4,4,256,128,0,129673,0.02,,227.4,37.0283,48.75,analytic,8388608,8421376,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S256_D128, +decode,0,16,4,4,512,128,0,147545,0.04,,399.0,42.1316,48.991,analytic,16777216,16809984,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S512_D128, +decode,0,16,4,4,1024,128,0,295824,0.04,,397.6,84.4728,89.19,analytic,33554432,33587200,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S1024_D128, +decode,0,16,4,4,2048,128,0,567046,0.05,,414.7,161.9205,167.369,analytic,67108864,67141632,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S2048_D128, +decode,0,16,4,4,4096,128,0,1112728,0.05,,422.5,317.7406,322.826,analytic,134217728,134250496,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S4096_D128, +decode,0,16,4,4,8192,128,0,2211617,0.05,,425.1,631.5297,637.471,analytic,268435456,268468224,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S8192_D128, +decode,0,16,4,4,16384,128,0,4385243,0.05,,428.8,1252.2111,1258.616,analytic,536870912,536903680,True,tb_logs/decode_gqa0_B16_Hq4_Hkv4_S16384_D128, +decode,0,16,8,8,128,128,0,237007,0.01,,124.9,67.6777,73.068,analytic,8388608,8454144,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S128_D128, +decode,0,16,8,8,256,128,0,255128,0.03,,231.2,72.852,78.204,analytic,16777216,16842752,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S256_D128, +decode,0,16,8,8,512,128,0,290388,0.04,,405.4,82.9206,88.499,analytic,33554432,33619968,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S512_D128, +decode,0,16,8,8,1024,128,0,583151,0.04,,403.4,166.5194,171.606,analytic,67108864,67174400,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S1024_D128, +decode,0,16,8,8,2048,128,0,1129463,0.05,,416.4,322.5193,327.526,analytic,134217728,134283264,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S2048_D128, +decode,0,16,8,8,4096,128,0,2223530,0.05,,422.9,634.9315,640.43,analytic,268435456,268500992,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S4096_D128, +decode,0,16,8,8,8192,128,0,4394058,0.05,,427.9,1254.7282,1260.803,analytic,536870912,536936448,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S8192_D128, +decode,0,16,8,8,16384,128,0,8774771,0.05,,428.6,2505.6455,2512.564,analytic,1073741824,1073807360,True,tb_logs/decode_gqa0_B16_Hq8_Hkv8_S16384_D128, +decode,0,32,1,1,128,128,0,63852,0.01,,115.9,18.233,35.164,analytic,2097152,2113536,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S128_D128, +decode,0,32,1,1,256,128,0,68782,0.02,,214.4,19.6408,41.869,analytic,4194304,4210688,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S256_D128, +decode,0,32,1,1,512,128,0,75992,0.04,,387.3,21.6996,48.012,analytic,8388608,8404992,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S512_D128, +decode,0,32,1,1,1024,128,0,151106,0.04,,389.2,43.1485,49.0,analytic,16777216,16793600,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S1024_D128, +decode,0,32,1,1,2048,128,0,289812,0.04,,405.7,82.7561,87.96,analytic,33554432,33570816,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S2048_D128, +decode,0,32,1,1,4096,128,0,562531,0.05,,417.9,160.6314,165.915,analytic,67108864,67125248,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S4096_D128, +decode,0,32,1,1,8192,128,0,1108601,0.05,,424.0,316.5623,322.12,analytic,134217728,134234112,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S8192_D128, +decode,0,32,1,1,16384,128,0,2207414,0.05,,425.9,630.3295,636.392,analytic,268435456,268451840,True,tb_logs/decode_gqa0_B32_Hq1_Hkv1_S16384_D128, +decode,0,32,4,4,128,128,0,237431,0.01,,124.7,67.7986,73.225,analytic,8388608,8454144,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S128_D128, +decode,0,32,4,4,256,128,0,263354,0.02,,224.0,75.2011,80.572,analytic,16777216,16842752,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S256_D128, +decode,0,32,4,4,512,128,0,291024,0.04,,404.6,83.1023,88.449,analytic,33554432,33619968,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S512_D128, +decode,0,32,4,4,1024,128,0,585472,0.04,,401.8,167.1821,172.205,analytic,67108864,67174400,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S1024_D128, +decode,0,32,4,4,2048,128,0,1131761,0.05,,415.5,323.1755,328.84,analytic,134217728,134283264,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S2048_D128, +decode,0,32,4,4,4096,128,0,2225601,0.05,,422.5,635.5227,641.446,analytic,268435456,268500992,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S4096_D128, +decode,0,32,4,4,8192,128,0,4395453,0.05,,427.8,1255.1266,1261.675,analytic,536870912,536936448,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S8192_D128, +decode,0,32,4,4,16384,128,0,8776275,0.05,,428.5,2506.075,2513.214,analytic,1073741824,1073807360,True,tb_logs/decode_gqa0_B32_Hq4_Hkv4_S16384_D128, +decode,0,32,8,8,128,128,0,479169,0.01,,123.6,136.8272,142.072,analytic,16777216,16908288,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S128_D128, +decode,0,32,8,8,256,128,0,507417,0.03,,232.5,144.8936,150.17,analytic,33554432,33685504,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S256_D128, +decode,0,32,8,8,512,128,0,576061,0.04,,408.8,164.4947,169.945,analytic,67108864,67239936,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S512_D128, +decode,0,32,8,8,1024,128,0,1162582,0.04,,404.7,331.9767,337.533,analytic,134217728,134348800,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S1024_D128, +decode,0,32,8,8,2048,128,0,2251540,0.05,,417.7,642.9299,649.057,analytic,268435456,268566528,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S2048_D128, +decode,0,32,8,8,4096,128,0,4417560,0.05,,425.7,1261.4392,1267.945,analytic,536870912,537001984,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S4096_D128, +decode,0,32,8,8,8192,128,0,8796274,0.05,,427.5,2511.7859,2519.538,analytic,1073741824,1073872896,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S8192_D128, +decode,0,32,8,8,16384,128,0,17603166,0.05,,427.2,5026.6037,5034.081,analytic,2147483648,2147614720,True,tb_logs/decode_gqa0_B32_Hq8_Hkv8_S16384_D128, +prefill,1,1,2,1,128,128,0,5764,1.11,,119.5,1.6459,48.825,analytic,16777216,196608,True,tb_logs/prefill_gqa1_B1_Hq2_Hkv1_S128_D128, +prefill,1,1,2,1,256,128,0,6816,3.76,,202.0,1.9463,47.355,analytic,67108864,393216,True,tb_logs/prefill_gqa1_B1_Hq2_Hkv1_S256_D128, +prefill,1,1,2,1,512,128,0,9053,11.31,,304.2,2.5851,47.412,analytic,268435456,786432,True,tb_logs/prefill_gqa1_B1_Hq2_Hkv1_S512_D128, +prefill,1,1,4,1,128,128,0,8816,1.45,,130.2,2.5173,47.204,analytic,33554432,327680,True,tb_logs/prefill_gqa1_B1_Hq4_Hkv1_S128_D128, +prefill,1,1,4,1,256,128,0,10466,4.89,,219.3,2.9885,47.58,analytic,134217728,655360,True,tb_logs/prefill_gqa1_B1_Hq4_Hkv1_S256_D128, +prefill,1,1,4,1,512,128,0,14145,14.48,,324.5,4.039,47.274,analytic,536870912,1310720,True,tb_logs/prefill_gqa1_B1_Hq4_Hkv1_S512_D128, +prefill,1,1,5,1,128,128,0,10339,1.55,,133.2,2.9523,47.634,analytic,41943040,393216,True,tb_logs/prefill_gqa1_B1_Hq5_Hkv1_S128_D128, +prefill,1,1,5,1,256,128,0,12308,5.2,,223.8,3.5145,47.64,analytic,167772160,786432,True,tb_logs/prefill_gqa1_B1_Hq5_Hkv1_S256_D128, +prefill,1,1,5,1,512,128,0,16713,15.32,,329.6,4.7723,48.925,analytic,671088640,1572864,True,tb_logs/prefill_gqa1_B1_Hq5_Hkv1_S512_D128, +prefill,1,1,8,1,128,128,0,14917,1.72,,138.5,4.2596,47.528,analytic,67108864,589824,True,tb_logs/prefill_gqa1_B1_Hq8_Hkv1_S128_D128, +prefill,1,1,8,1,256,128,0,17887,5.72,,231.0,5.1077,47.182,analytic,268435456,1179648,True,tb_logs/prefill_gqa1_B1_Hq8_Hkv1_S256_D128, +prefill,1,1,8,1,512,128,0,24322,16.84,,339.7,6.9453,47.811,analytic,1073741824,2359296,True,tb_logs/prefill_gqa1_B1_Hq8_Hkv1_S512_D128, +prefill,1,1,16,1,128,128,0,27222,1.88,,143.3,7.7732,32.212,analytic,134217728,1114112,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv1_S128_D128, +prefill,1,1,16,1,256,128,0,32725,6.26,,238.5,9.3445,32.161,analytic,536870912,2228224,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv1_S256_D128, +prefill,1,1,16,1,512,128,0,45230,18.11,,345.0,12.9154,32.128,analytic,2147483648,4456448,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv1_S512_D128, +prefill,1,1,16,2,128,128,0,27214,1.88,,151.8,7.771,32.019,analytic,134217728,1179648,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv2_S128_D128, +prefill,1,1,16,2,256,128,0,32730,6.26,,252.4,9.346,32.209,analytic,536870912,2359296,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv2_S256_D128, +prefill,1,1,16,2,512,128,0,45252,18.1,,365.2,12.9216,33.22,analytic,2147483648,4718592,True,tb_logs/prefill_gqa1_B1_Hq16_Hkv2_S512_D128, +prefill,1,1,12,4,128,128,0,21034,1.83,,174.6,6.0063,32.067,analytic,100663296,1048576,True,tb_logs/prefill_gqa1_B1_Hq12_Hkv4_S128_D128, +prefill,1,1,12,4,256,128,0,25173,6.1,,291.8,7.1881,32.302,analytic,402653184,2097152,True,tb_logs/prefill_gqa1_B1_Hq12_Hkv4_S256_D128, +prefill,1,1,12,4,512,128,0,34629,17.74,,424.2,9.8884,32.501,analytic,1610612736,4194304,True,tb_logs/prefill_gqa1_B1_Hq12_Hkv4_S512_D128, +prefill,1,1,32,4,128,128,0,52101,1.97,,158.6,14.8776,32.296,analytic,268435456,2359296,True,tb_logs/prefill_gqa1_B1_Hq32_Hkv4_S128_D128, +prefill,1,1,32,4,256,128,0,62854,6.52,,262.9,17.9482,32.511,analytic,1073741824,4718592,True,tb_logs/prefill_gqa1_B1_Hq32_Hkv4_S256_D128, +prefill,1,1,32,4,512,128,0,87511,18.72,,377.7,24.9889,33.865,analytic,4294967296,9437184,True,tb_logs/prefill_gqa1_B1_Hq32_Hkv4_S512_D128, +prefill,1,1,64,8,128,128,0,101994,2.01,,162.0,29.1246,47.429,analytic,536870912,4718592,True,tb_logs/prefill_gqa1_B1_Hq64_Hkv8_S128_D128, +prefill,1,1,64,8,256,128,0,123330,6.64,,268.0,35.217,48.915,analytic,2147483648,9437184,True,tb_logs/prefill_gqa1_B1_Hq64_Hkv8_S256_D128, +prefill,1,1,64,8,512,128,0,169529,19.33,,389.9,48.4093,62.124,analytic,8589934592,18874368,True,tb_logs/prefill_gqa1_B1_Hq64_Hkv8_S512_D128, +prefill,1,2,2,1,128,128,0,9439,1.36,,145.9,2.6953,47.557,analytic,33554432,393216,True,tb_logs/prefill_gqa1_B2_Hq2_Hkv1_S128_D128, +prefill,1,2,2,1,256,128,0,11189,4.58,,246.2,3.1949,47.543,analytic,134217728,786432,True,tb_logs/prefill_gqa1_B2_Hq2_Hkv1_S256_D128, +prefill,1,2,2,1,512,128,0,15138,13.53,,363.9,4.3226,47.77,analytic,536870912,1572864,True,tb_logs/prefill_gqa1_B2_Hq2_Hkv1_S512_D128, +prefill,1,2,4,1,128,128,0,15955,1.6,,143.8,4.5559,48.44,analytic,67108864,655360,True,tb_logs/prefill_gqa1_B2_Hq4_Hkv1_S128_D128, +prefill,1,2,4,1,256,128,0,18909,5.42,,242.8,5.3994,48.339,analytic,268435456,1310720,True,tb_logs/prefill_gqa1_B2_Hq4_Hkv1_S256_D128, +prefill,1,2,4,1,512,128,0,25750,15.91,,356.5,7.3529,48.98,analytic,1073741824,2621440,True,tb_logs/prefill_gqa1_B2_Hq4_Hkv1_S512_D128, +prefill,1,2,5,1,128,128,0,19163,1.67,,143.7,5.4719,48.941,analytic,83886080,786432,True,tb_logs/prefill_gqa1_B2_Hq5_Hkv1_S128_D128, +prefill,1,2,5,1,256,128,0,22732,5.63,,242.3,6.4912,47.417,analytic,335544320,1572864,True,tb_logs/prefill_gqa1_B2_Hq5_Hkv1_S256_D128, +prefill,1,2,5,1,512,128,0,31022,16.5,,355.1,8.8585,48.672,analytic,1342177280,3145728,True,tb_logs/prefill_gqa1_B2_Hq5_Hkv1_S512_D128, +prefill,1,2,8,1,128,128,0,29186,1.75,,141.5,8.3342,48.642,analytic,134217728,1179648,True,tb_logs/prefill_gqa1_B2_Hq8_Hkv1_S128_D128, +prefill,1,2,8,1,256,128,0,34532,5.93,,239.3,9.8607,46.675,analytic,536870912,2359296,True,tb_logs/prefill_gqa1_B2_Hq8_Hkv1_S256_D128, +prefill,1,2,8,1,512,128,0,47309,17.32,,349.3,13.509,47.389,analytic,2147483648,4718592,True,tb_logs/prefill_gqa1_B2_Hq8_Hkv1_S512_D128, +prefill,1,2,16,1,128,128,0,55682,1.84,,140.1,15.9002,47.59,analytic,268435456,2228224,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv1_S128_D128, +prefill,1,2,16,1,256,128,0,65921,6.21,,236.7,18.8239,47.448,analytic,1073741824,4456448,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv1_S256_D128, +prefill,1,2,16,1,512,128,0,90916,18.02,,343.3,25.9612,47.634,analytic,4294967296,8912896,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv1_S512_D128, +prefill,1,2,16,2,128,128,0,55679,1.84,,148.4,15.8993,47.619,analytic,268435456,2359296,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv2_S128_D128, +prefill,1,2,16,2,256,128,0,65940,6.21,,250.6,18.8291,47.519,analytic,1073741824,4718592,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv2_S256_D128, +prefill,1,2,16,2,512,128,0,90999,18.01,,363.2,25.985,47.576,analytic,4294967296,9437184,True,tb_logs/prefill_gqa1_B2_Hq16_Hkv2_S512_D128, +prefill,1,2,12,4,128,128,0,42235,1.82,,173.9,12.0603,46.721,analytic,201326592,2097152,True,tb_logs/prefill_gqa1_B2_Hq12_Hkv4_S128_D128, +prefill,1,2,12,4,256,128,0,50200,6.12,,292.6,14.3348,46.636,analytic,805306368,4194304,True,tb_logs/prefill_gqa1_B2_Hq12_Hkv4_S256_D128, +prefill,1,2,12,4,512,128,0,69292,17.73,,424.0,19.7864,46.635,analytic,3221225472,8388608,True,tb_logs/prefill_gqa1_B2_Hq12_Hkv4_S512_D128, +prefill,1,2,32,4,128,128,0,108813,1.88,,151.9,31.0718,48.622,analytic,536870912,4718592,True,tb_logs/prefill_gqa1_B2_Hq32_Hkv4_S128_D128, +prefill,1,2,32,4,256,128,0,128952,6.35,,256.3,36.8224,50.565,analytic,2147483648,9437184,True,tb_logs/prefill_gqa1_B2_Hq32_Hkv4_S256_D128, +prefill,1,2,32,4,512,128,0,176116,18.61,,375.3,50.29,62.981,analytic,8589934592,18874368,True,tb_logs/prefill_gqa1_B2_Hq32_Hkv4_S512_D128, +prefill,1,2,64,8,128,128,0,215708,1.9,,153.2,61.5955,83.476,analytic,1073741824,9437184,True,tb_logs/prefill_gqa1_B2_Hq64_Hkv8_S128_D128, +prefill,1,2,64,8,256,128,0,251929,6.5,,262.4,71.9387,95.234,analytic,4294967296,18874368,True,tb_logs/prefill_gqa1_B2_Hq64_Hkv8_S256_D128, +prefill,1,2,64,8,512,128,0,347432,18.86,,380.5,99.2097,119.888,analytic,17179869184,37748736,True,tb_logs/prefill_gqa1_B2_Hq64_Hkv8_S512_D128, +prefill,1,4,2,1,128,128,0,16356,1.57,,168.4,4.6706,48.604,analytic,67108864,786432,True,tb_logs/prefill_gqa1_B4_Hq2_Hkv1_S128_D128, +prefill,1,4,2,1,256,128,0,19667,5.21,,280.1,5.6158,34.018,analytic,268435456,1572864,True,tb_logs/prefill_gqa1_B4_Hq2_Hkv1_S256_D128, +prefill,1,4,2,1,512,128,0,26928,15.21,,409.1,7.6892,33.816,analytic,1073741824,3145728,True,tb_logs/prefill_gqa1_B4_Hq2_Hkv1_S512_D128, +prefill,1,4,4,1,128,128,0,29546,1.73,,155.4,8.437,33.816,analytic,134217728,1310720,True,tb_logs/prefill_gqa1_B4_Hq4_Hkv1_S128_D128, +prefill,1,4,4,1,256,128,0,35250,5.81,,260.4,10.0655,34.004,analytic,536870912,2621440,True,tb_logs/prefill_gqa1_B4_Hq4_Hkv1_S256_D128, +prefill,1,4,4,1,512,128,0,48537,16.88,,378.3,13.8597,33.884,analytic,2147483648,5242880,True,tb_logs/prefill_gqa1_B4_Hq4_Hkv1_S512_D128, +prefill,1,4,5,1,128,128,0,36010,1.78,,153.0,10.2827,33.859,analytic,167772160,1572864,True,tb_logs/prefill_gqa1_B4_Hq5_Hkv1_S128_D128, +prefill,1,4,5,1,256,128,0,43058,5.95,,255.8,12.2952,34.034,analytic,671088640,3145728,True,tb_logs/prefill_gqa1_B4_Hq5_Hkv1_S256_D128, +prefill,1,4,5,1,512,128,0,59462,17.22,,370.5,16.9796,33.645,analytic,2684354560,6291456,True,tb_logs/prefill_gqa1_B4_Hq5_Hkv1_S512_D128, +prefill,1,4,8,1,128,128,0,56065,1.83,,147.4,16.0095,33.745,analytic,268435456,2359296,True,tb_logs/prefill_gqa1_B4_Hq8_Hkv1_S128_D128, +prefill,1,4,8,1,256,128,0,66785,6.13,,247.4,19.0706,48.1,analytic,1073741824,4718592,True,tb_logs/prefill_gqa1_B4_Hq8_Hkv1_S256_D128, +prefill,1,4,8,1,512,128,0,92239,17.76,,358.3,26.339,48.384,analytic,4294967296,9437184,True,tb_logs/prefill_gqa1_B4_Hq8_Hkv1_S512_D128, +prefill,1,4,16,1,128,128,0,109269,1.87,,142.8,31.2019,48.641,analytic,536870912,4456448,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv1_S128_D128, +prefill,1,4,16,1,256,128,0,129744,6.31,,240.6,37.0484,50.059,analytic,2147483648,8912896,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv1_S256_D128, +prefill,1,4,16,1,512,128,0,177324,18.48,,352.0,50.6352,63.163,analytic,8589934592,17825792,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv1_S512_D128, +prefill,1,4,16,2,128,128,0,109232,1.87,,151.3,31.1914,48.726,analytic,536870912,4718592,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv2_S128_D128, +prefill,1,4,16,2,256,128,0,129804,6.31,,254.6,37.0655,50.938,analytic,2147483648,9437184,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv2_S256_D128, +prefill,1,4,16,2,512,128,0,177444,18.47,,372.5,50.6693,64.06,analytic,8589934592,18874368,True,tb_logs/prefill_gqa1_B4_Hq16_Hkv2_S512_D128, +prefill,1,4,12,4,128,128,0,82295,1.87,,178.5,23.4993,48.017,analytic,402653184,4194304,True,tb_logs/prefill_gqa1_B4_Hq12_Hkv4_S128_D128, +prefill,1,4,12,4,256,128,0,98178,6.26,,299.2,28.035,48.967,analytic,1610612736,8388608,True,tb_logs/prefill_gqa1_B4_Hq12_Hkv4_S256_D128, +prefill,1,4,12,4,512,128,0,136206,18.04,,431.4,38.8937,51.993,analytic,6442450944,16777216,True,tb_logs/prefill_gqa1_B4_Hq12_Hkv4_S512_D128, +prefill,1,4,32,4,128,128,0,216127,1.9,,152.9,61.7152,84.158,analytic,1073741824,9437184,True,tb_logs/prefill_gqa1_B4_Hq32_Hkv4_S128_D128, +prefill,1,4,32,4,256,128,0,252744,6.48,,261.5,72.1713,94.928,analytic,4294967296,18874368,True,tb_logs/prefill_gqa1_B4_Hq32_Hkv4_S256_D128, +prefill,1,4,32,4,512,128,0,348736,18.79,,379.1,99.582,120.688,analytic,17179869184,37748736,True,tb_logs/prefill_gqa1_B4_Hq32_Hkv4_S512_D128, +prefill,1,4,64,8,128,128,0,424494,1.93,,155.7,121.2148,161.593,analytic,2147483648,18874368,True,tb_logs/prefill_gqa1_B4_Hq64_Hkv8_S128_D128, +prefill,1,4,64,8,256,128,0,500863,6.54,,263.9,143.0221,185.045,analytic,8589934592,37748736,True,tb_logs/prefill_gqa1_B4_Hq64_Hkv8_S256_D128, +prefill,1,4,64,8,512,128,0,691117,18.97,,382.6,197.3491,234.145,analytic,34359738368,75497472,True,tb_logs/prefill_gqa1_B4_Hq64_Hkv8_S512_D128, +prefill,1,8,2,1,128,128,0,30470,1.68,,180.8,8.7006,35.407,analytic,134217728,1572864,True,tb_logs/prefill_gqa1_B8_Hq2_Hkv1_S128_D128, +prefill,1,8,2,1,256,128,0,37026,5.53,,297.5,10.5727,33.512,analytic,536870912,3145728,True,tb_logs/prefill_gqa1_B8_Hq2_Hkv1_S256_D128, +prefill,1,8,2,1,512,128,0,51228,15.99,,430.1,14.6281,47.45,analytic,2147483648,6291456,True,tb_logs/prefill_gqa1_B8_Hq2_Hkv1_S512_D128, +prefill,1,8,4,1,128,128,0,57002,1.8,,161.1,16.2771,47.652,analytic,268435456,2621440,True,tb_logs/prefill_gqa1_B8_Hq4_Hkv1_S128_D128, +prefill,1,8,4,1,256,128,0,68332,5.99,,268.7,19.5123,46.654,analytic,1073741824,5242880,True,tb_logs/prefill_gqa1_B8_Hq4_Hkv1_S256_D128, +prefill,1,8,4,1,512,128,0,94787,17.29,,387.4,27.0666,38.285,analytic,4294967296,10485760,True,tb_logs/prefill_gqa1_B8_Hq4_Hkv1_S512_D128, +prefill,1,8,5,1,128,128,0,69949,1.83,,157.5,19.974,32.848,analytic,335544320,3145728,True,tb_logs/prefill_gqa1_B8_Hq5_Hkv1_S128_D128, +prefill,1,8,5,1,256,128,0,83982,6.1,,262.4,23.981,35.171,analytic,1342177280,6291456,True,tb_logs/prefill_gqa1_B8_Hq5_Hkv1_S256_D128, +prefill,1,8,5,1,512,128,0,116649,17.56,,377.8,33.3092,48.573,analytic,5368709120,12582912,True,tb_logs/prefill_gqa1_B8_Hq5_Hkv1_S512_D128, +prefill,1,8,8,1,128,128,0,110005,1.86,,150.2,31.412,44.33,analytic,536870912,4718592,True,tb_logs/prefill_gqa1_B8_Hq8_Hkv1_S128_D128, +prefill,1,8,8,1,256,128,0,131288,6.24,,251.7,37.4895,50.207,analytic,2147483648,9437184,True,tb_logs/prefill_gqa1_B8_Hq8_Hkv1_S256_D128, +prefill,1,8,8,1,512,128,0,179839,18.22,,367.5,51.3532,64.415,analytic,8589934592,18874368,True,tb_logs/prefill_gqa1_B8_Hq8_Hkv1_S512_D128, +prefill,1,8,16,1,128,128,0,216831,1.89,,144.0,61.9162,83.099,analytic,1073741824,8912896,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv1_S128_D128, +prefill,1,8,16,1,256,128,0,254247,6.44,,245.5,72.6005,93.839,analytic,4294967296,17825792,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv1_S256_D128, +prefill,1,8,16,1,512,128,0,351015,18.67,,355.7,100.2327,120.058,analytic,17179869184,35651584,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv1_S512_D128, +prefill,1,8,16,2,128,128,0,216884,1.89,,152.4,61.9316,83.397,analytic,1073741824,9437184,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv2_S128_D128, +prefill,1,8,16,2,256,128,0,254315,6.44,,259.9,72.6198,94.808,analytic,4294967296,18874368,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv2_S256_D128, +prefill,1,8,16,2,512,128,0,351140,18.66,,376.5,100.2684,121.456,analytic,17179869184,37748736,True,tb_logs/prefill_gqa1_B8_Hq16_Hkv2_S512_D128, +prefill,1,8,12,4,128,128,0,162458,1.89,,180.8,46.3901,64.646,analytic,805306368,8388608,True,tb_logs/prefill_gqa1_B8_Hq12_Hkv4_S128_D128, +prefill,1,8,12,4,256,128,0,194229,6.33,,302.5,55.4624,74.602,analytic,3221225472,16777216,True,tb_logs/prefill_gqa1_B8_Hq12_Hkv4_S256_D128, +prefill,1,8,12,4,512,128,0,265929,18.48,,441.9,75.9365,96.696,analytic,12884901888,33554432,True,tb_logs/prefill_gqa1_B8_Hq12_Hkv4_S512_D128, +prefill,1,8,32,4,128,128,0,425214,1.93,,155.4,121.4204,161.84,analytic,2147483648,18874368,True,tb_logs/prefill_gqa1_B8_Hq32_Hkv4_S128_D128, +prefill,1,8,32,4,256,128,0,502368,6.52,,263.1,143.4517,184.599,analytic,8589934592,37748736,True,tb_logs/prefill_gqa1_B8_Hq32_Hkv4_S256_D128, +prefill,1,8,32,4,512,128,0,693629,18.9,,381.2,198.0665,235.014,analytic,34359738368,75497472,True,tb_logs/prefill_gqa1_B8_Hq32_Hkv4_S512_D128, +prefill,1,8,64,8,128,128,0,846067,1.94,,156.2,241.5953,319.182,analytic,4294967296,37748736,True,tb_logs/prefill_gqa1_B8_Hq64_Hkv8_S128_D128, +prefill,1,8,64,8,256,128,0,998579,6.56,,264.8,285.1454,364.258,analytic,17179869184,75497472,True,tb_logs/prefill_gqa1_B8_Hq64_Hkv8_S256_D128, +prefill,1,8,64,8,512,128,0,1522511,17.22,,347.3,434.7546,462.536,analytic,68719476736,150994944,True,tb_logs/prefill_gqa1_B8_Hq64_Hkv8_S512_D128, +prefill,1,16,2,1,128,128,0,58735,1.74,,187.6,16.7717,47.506,analytic,268435456,3145728,True,tb_logs/prefill_gqa1_B16_Hq2_Hkv1_S128_D128, +prefill,1,16,2,1,256,128,0,71669,5.72,,307.4,20.465,47.393,analytic,1073741824,6291456,True,tb_logs/prefill_gqa1_B16_Hq2_Hkv1_S256_D128, +prefill,1,16,2,1,512,128,0,99876,16.4,,441.2,28.5198,43.5,analytic,4294967296,12582912,True,tb_logs/prefill_gqa1_B16_Hq2_Hkv1_S512_D128, +prefill,1,16,4,1,128,128,0,111840,1.83,,164.2,31.9361,46.304,analytic,536870912,5242880,True,tb_logs/prefill_gqa1_B16_Hq4_Hkv1_S128_D128, +prefill,1,16,4,1,256,128,0,134722,6.08,,272.6,38.4701,52.717,analytic,2147483648,10485760,True,tb_logs/prefill_gqa1_B16_Hq4_Hkv1_S256_D128, +prefill,1,16,4,1,512,128,0,184922,17.72,,397.2,52.8046,68.218,analytic,8589934592,20971520,True,tb_logs/prefill_gqa1_B16_Hq4_Hkv1_S512_D128, +prefill,1,16,5,1,128,128,0,137864,1.86,,159.8,39.3671,54.619,analytic,671088640,6291456,True,tb_logs/prefill_gqa1_B16_Hq5_Hkv1_S128_D128, +prefill,1,16,5,1,256,128,0,165903,6.17,,265.6,47.3737,62.6,analytic,2684354560,12582912,True,tb_logs/prefill_gqa1_B16_Hq5_Hkv1_S256_D128, +prefill,1,16,5,1,512,128,0,227797,17.98,,386.9,65.0476,82.319,analytic,10737418240,25165824,True,tb_logs/prefill_gqa1_B16_Hq5_Hkv1_S512_D128, +prefill,1,16,8,1,128,128,0,218732,1.87,,151.1,62.4591,83.881,analytic,1073741824,9437184,True,tb_logs/prefill_gqa1_B16_Hq8_Hkv1_S128_D128, +prefill,1,16,8,1,256,128,0,257619,6.36,,256.6,73.5634,95.805,analytic,4294967296,18874368,True,tb_logs/prefill_gqa1_B16_Hq8_Hkv1_S256_D128, +prefill,1,16,8,1,512,128,0,356115,18.4,,371.2,101.689,124.296,analytic,17179869184,37748736,True,tb_logs/prefill_gqa1_B16_Hq8_Hkv1_S512_D128, +prefill,1,16,16,1,128,128,0,427009,1.92,,146.2,121.9328,161.808,analytic,2147483648,17825792,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv1_S128_D128, +prefill,1,16,16,1,256,128,0,505523,6.48,,247.0,144.3526,182.901,analytic,8589934592,35651584,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv1_S256_D128, +prefill,1,16,16,1,512,128,0,698336,18.77,,357.6,199.4107,235.132,analytic,34359738368,71303168,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv1_S512_D128, +prefill,1,16,16,2,128,128,0,427119,1.92,,154.8,121.9642,162.815,analytic,2147483648,18874368,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv2_S128_D128, +prefill,1,16,16,2,256,128,0,505581,6.48,,261.5,144.3692,185.495,analytic,8589934592,37748736,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv2_S256_D128, +prefill,1,16,16,2,512,128,0,698451,18.77,,378.5,199.4436,237.87,analytic,34359738368,75497472,True,tb_logs/prefill_gqa1_B16_Hq16_Hkv2_S512_D128, +prefill,1,16,12,4,128,128,0,324609,1.89,,181.0,92.6924,125.144,analytic,1610612736,16777216,True,tb_logs/prefill_gqa1_B16_Hq12_Hkv4_S128_D128, +prefill,1,16,12,4,256,128,0,381459,6.44,,308.0,108.926,145.193,analytic,6442450944,33554432,True,tb_logs/prefill_gqa1_B16_Hq12_Hkv4_S256_D128, +prefill,1,16,12,4,512,128,0,528359,18.61,,444.8,150.8735,187.878,analytic,25769803776,67108864,True,tb_logs/prefill_gqa1_B16_Hq12_Hkv4_S512_D128, +prefill,1,16,32,4,128,128,0,847685,1.93,,155.9,242.0575,319.473,analytic,4294967296,37748736,True,tb_logs/prefill_gqa1_B16_Hq32_Hkv4_S128_D128, +prefill,1,16,32,4,256,128,0,1001787,6.54,,263.9,286.0613,364.453,analytic,17179869184,75497472,True,tb_logs/prefill_gqa1_B16_Hq32_Hkv4_S256_D128, +prefill,1,16,32,4,512,128,0,1528423,17.15,,346.0,436.4429,463.975,analytic,68719476736,150994944,True,tb_logs/prefill_gqa1_B16_Hq32_Hkv4_S512_D128, +prefill,1,16,64,8,128,128,0,1689292,1.94,,156.5,482.3792,633.092,analytic,8589934592,75497472,True,tb_logs/prefill_gqa1_B16_Hq64_Hkv8_S128_D128, +prefill,1,16,64,8,256,128,0,2170948,6.04,,243.6,619.9165,723.018,analytic,34359738368,150994944,True,tb_logs/prefill_gqa1_B16_Hq64_Hkv8_S256_D128, +prefill,1,16,64,8,512,128,0,3592953,14.59,,294.3,1025.9717,919.674,analytic,137438953472,301989888,True,tb_logs/prefill_gqa1_B16_Hq64_Hkv8_S512_D128, +prefill,1,32,2,1,128,128,0,115261,1.78,,191.2,32.913,46.446,analytic,536870912,6291456,True,tb_logs/prefill_gqa1_B32_Hq2_Hkv1_S128_D128, +prefill,1,32,2,1,256,128,0,141385,5.79,,311.7,40.3727,54.595,analytic,2147483648,12582912,True,tb_logs/prefill_gqa1_B32_Hq2_Hkv1_S256_D128, +prefill,1,32,2,1,512,128,0,194633,16.84,,452.8,55.5777,76.316,analytic,8589934592,25165824,True,tb_logs/prefill_gqa1_B32_Hq2_Hkv1_S512_D128, +prefill,1,32,4,1,128,128,0,222356,1.84,,165.1,63.4939,85.448,analytic,1073741824,10485760,True,tb_logs/prefill_gqa1_B32_Hq4_Hkv1_S128_D128, +prefill,1,32,4,1,256,128,0,264008,6.21,,278.2,75.3879,98.979,analytic,4294967296,20971520,True,tb_logs/prefill_gqa1_B32_Hq4_Hkv1_S256_D128, +prefill,1,32,4,1,512,128,0,366233,17.9,,401.1,104.5783,132.445,analytic,17179869184,41943040,True,tb_logs/prefill_gqa1_B32_Hq4_Hkv1_S512_D128, +prefill,1,32,5,1,128,128,0,275056,1.86,,160.2,78.5425,104.867,analytic,1342177280,12582912,True,tb_logs/prefill_gqa1_B32_Hq5_Hkv1_S128_D128, +prefill,1,32,5,1,256,128,0,326094,6.28,,270.3,93.1166,120.562,analytic,5368709120,25165824,True,tb_logs/prefill_gqa1_B32_Hq5_Hkv1_S256_D128, +prefill,1,32,5,1,512,128,0,451987,18.12,,390.0,129.0655,159.902,analytic,21474836480,50331648,True,tb_logs/prefill_gqa1_B32_Hq5_Hkv1_S512_D128, +prefill,1,32,8,1,128,128,0,430141,1.9,,153.7,122.8273,163.401,analytic,2147483648,18874368,True,tb_logs/prefill_gqa1_B32_Hq8_Hkv1_S128_D128, +prefill,1,32,8,1,256,128,0,512160,6.4,,258.1,146.2479,187.03,analytic,8589934592,37748736,True,tb_logs/prefill_gqa1_B32_Hq8_Hkv1_S256_D128, +prefill,1,32,8,1,512,128,0,708410,18.5,,373.2,202.2873,244.742,analytic,34359738368,75497472,True,tb_logs/prefill_gqa1_B32_Hq8_Hkv1_S512_D128, +prefill,1,32,16,1,128,128,0,851196,1.92,,146.7,243.0599,318.824,analytic,4294967296,35651584,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv1_S128_D128, +prefill,1,32,16,1,256,128,0,1007898,6.5,,247.7,287.8063,376.368,analytic,17179869184,71303168,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv1_S256_D128, +prefill,1,32,16,1,512,128,0,1539053,17.03,,324.5,439.4782,464.967,analytic,68719476736,142606336,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv1_S512_D128, +prefill,1,32,16,2,128,128,0,851305,1.92,,155.3,243.0911,320.227,analytic,4294967296,37748736,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv2_S128_D128, +prefill,1,32,16,2,256,128,0,1008233,6.5,,262.2,287.902,366.819,analytic,17179869184,75497472,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv2_S256_D128, +prefill,1,32,16,2,512,128,0,1539908,17.02,,343.4,439.7225,470.428,analytic,68719476736,150994944,True,tb_logs/prefill_gqa1_B32_Hq16_Hkv2_S512_D128, +prefill,1,32,12,4,128,128,0,638313,1.93,,184.1,182.271,246.259,analytic,3221225472,33554432,True,tb_logs/prefill_gqa1_B32_Hq12_Hkv4_S128_D128, +prefill,1,32,12,4,256,128,0,760100,6.47,,309.2,217.0474,285.744,analytic,12884901888,67108864,True,tb_logs/prefill_gqa1_B32_Hq12_Hkv4_S256_D128, +prefill,1,32,12,4,512,128,0,1136595,17.3,,413.5,324.5558,372.358,analytic,51539607552,134217728,True,tb_logs/prefill_gqa1_B32_Hq12_Hkv4_S512_D128, +prefill,1,32,32,4,128,128,0,1693105,1.94,,156.2,483.468,636.734,analytic,8589934592,75497472,True,tb_logs/prefill_gqa1_B32_Hq32_Hkv4_S128_D128, +prefill,1,32,32,4,256,128,0,2177243,6.02,,242.9,621.7141,725.741,analytic,34359738368,150994944,True,tb_logs/prefill_gqa1_B32_Hq32_Hkv4_S256_D128, +prefill,1,32,32,4,512,128,0,3612301,14.51,,292.8,1031.4965,928.094,analytic,137438953472,301989888,True,tb_logs/prefill_gqa1_B32_Hq32_Hkv4_S512_D128, +prefill,1,32,64,8,128,128,0,3615023,1.81,,146.3,1032.2739,1264.623,analytic,17179869184,150994944,True,tb_logs/prefill_gqa1_B32_Hq64_Hkv8_S128_D128, +prefill,1,32,64,8,256,128,0,5315519,4.93,,199.0,1517.8522,1433.818,analytic,68719476736,301989888,True,tb_logs/prefill_gqa1_B32_Hq64_Hkv8_S256_D128, +prefill,1,32,64,8,512,128,0,7181610,14.6,,294.5,2050.7167,1831.213,analytic,274877906944,603979776,True,tb_logs/prefill_gqa1_B32_Hq64_Hkv8_S512_D128, +decode,1,1,2,1,128,128,0,7470,0.01,,31.2,2.133,47.282,analytic,131072,66560,True,tb_logs/decode_gqa1_B1_Hq2_Hkv1_S128_D128, +decode,1,1,2,1,256,128,0,7481,0.01,,61.8,2.1361,47.365,analytic,262144,132096,True,tb_logs/decode_gqa1_B1_Hq2_Hkv1_S256_D128, +decode,1,1,2,1,512,128,0,8252,0.02,,111.7,2.3565,47.436,analytic,524288,263168,True,tb_logs/decode_gqa1_B1_Hq2_Hkv1_S512_D128, +decode,1,1,4,1,128,128,0,9877,0.01,,24.0,2.8204,47.084,analytic,262144,67584,True,tb_logs/decode_gqa1_B1_Hq4_Hkv1_S128_D128, +decode,1,1,4,1,256,128,0,10111,0.02,,46.1,2.8871,47.408,analytic,524288,133120,True,tb_logs/decode_gqa1_B1_Hq4_Hkv1_S256_D128, +decode,1,1,4,1,512,128,0,11162,0.04,,82.9,3.1873,47.529,analytic,1048576,264192,True,tb_logs/decode_gqa1_B1_Hq4_Hkv1_S512_D128, +decode,1,1,5,1,128,128,0,11185,0.01,,21.3,3.1939,47.869,analytic,327680,68096,True,tb_logs/decode_gqa1_B1_Hq5_Hkv1_S128_D128, +decode,1,1,5,1,256,128,0,11576,0.02,,40.4,3.3057,47.472,analytic,655360,133632,True,tb_logs/decode_gqa1_B1_Hq5_Hkv1_S256_D128, +decode,1,1,5,1,512,128,0,12571,0.04,,73.7,3.5896,47.363,analytic,1310720,264704,True,tb_logs/decode_gqa1_B1_Hq5_Hkv1_S512_D128, +decode,1,1,8,1,128,128,0,14973,0.01,,16.3,4.2756,47.57,analytic,524288,69632,True,tb_logs/decode_gqa1_B1_Hq8_Hkv1_S128_D128, +decode,1,1,8,1,256,128,0,15458,0.03,,30.6,4.4139,46.555,analytic,1048576,135168,True,tb_logs/decode_gqa1_B1_Hq8_Hkv1_S256_D128, +decode,1,1,8,1,512,128,0,17021,0.05,,54.8,4.8605,47.366,analytic,2097152,266240,True,tb_logs/decode_gqa1_B1_Hq8_Hkv1_S512_D128, +decode,1,1,16,1,128,128,0,25252,0.02,,10.2,7.2109,47.658,analytic,1048576,73728,True,tb_logs/decode_gqa1_B1_Hq16_Hkv1_S128_D128, +decode,1,1,16,1,256,128,0,25791,0.03,,18.9,7.3646,47.286,analytic,2097152,139264,True,tb_logs/decode_gqa1_B1_Hq16_Hkv1_S256_D128, +decode,1,1,16,1,512,128,0,28669,0.06,,33.0,8.1863,47.284,analytic,4194304,270336,True,tb_logs/decode_gqa1_B1_Hq16_Hkv1_S512_D128, +decode,1,1,16,2,128,128,0,26682,0.01,,18.3,7.619,48.313,analytic,1048576,139264,True,tb_logs/decode_gqa1_B1_Hq16_Hkv2_S128_D128, +decode,1,1,16,2,256,128,0,27270,0.03,,34.7,7.787,48.0,analytic,2097152,270336,True,tb_logs/decode_gqa1_B1_Hq16_Hkv2_S256_D128, +decode,1,1,16,2,512,128,0,30061,0.05,,62.0,8.5839,47.575,analytic,4194304,532480,True,tb_logs/decode_gqa1_B1_Hq16_Hkv2_S512_D128, +decode,1,1,12,4,128,128,0,23432,0.01,,40.1,6.691,47.802,analytic,786432,268288,True,tb_logs/decode_gqa1_B1_Hq12_Hkv4_S128_D128, +decode,1,1,12,4,256,128,0,24533,0.02,,75.7,7.0055,48.589,analytic,1572864,530432,True,tb_logs/decode_gqa1_B1_Hq12_Hkv4_S256_D128, +decode,1,1,12,4,512,128,0,27115,0.04,,136.2,7.7426,49.082,analytic,3145728,1054720,True,tb_logs/decode_gqa1_B1_Hq12_Hkv4_S512_D128, +decode,1,1,32,4,128,128,0,49170,0.02,,19.8,14.0406,49.264,analytic,2097152,278528,True,tb_logs/decode_gqa1_B1_Hq32_Hkv4_S128_D128, +decode,1,1,32,4,256,128,0,50765,0.03,,37.3,14.496,48.812,analytic,4194304,540672,True,tb_logs/decode_gqa1_B1_Hq32_Hkv4_S256_D128, +decode,1,1,32,4,512,128,0,56287,0.06,,66.3,16.073,48.785,analytic,8388608,1064960,True,tb_logs/decode_gqa1_B1_Hq32_Hkv4_S512_D128, +decode,1,1,64,8,128,128,0,94980,0.02,,20.5,27.1216,51.391,analytic,4194304,557056,True,tb_logs/decode_gqa1_B1_Hq64_Hkv8_S128_D128, +decode,1,1,64,8,256,128,0,97435,0.03,,38.9,27.8226,48.455,analytic,8388608,1081344,True,tb_logs/decode_gqa1_B1_Hq64_Hkv8_S256_D128, +decode,1,1,64,8,512,128,0,109755,0.06,,68.0,31.3407,49.073,analytic,16777216,2129920,True,tb_logs/decode_gqa1_B1_Hq64_Hkv8_S512_D128, +decode,1,2,2,1,128,128,0,10944,0.01,,42.6,3.125,33.46,analytic,262144,133120,True,tb_logs/decode_gqa1_B2_Hq2_Hkv1_S128_D128, +decode,1,2,2,1,256,128,0,11467,0.02,,80.7,3.2744,32.641,analytic,524288,264192,True,tb_logs/decode_gqa1_B2_Hq2_Hkv1_S256_D128, +decode,1,2,2,1,512,128,0,12558,0.03,,146.8,3.5858,32.565,analytic,1048576,526336,True,tb_logs/decode_gqa1_B2_Hq2_Hkv1_S512_D128, +decode,1,2,4,1,128,128,0,16079,0.01,,29.4,4.5913,34.969,analytic,524288,135168,True,tb_logs/decode_gqa1_B2_Hq4_Hkv1_S128_D128, +decode,1,2,4,1,256,128,0,16856,0.02,,55.3,4.8133,32.24,analytic,1048576,266240,True,tb_logs/decode_gqa1_B2_Hq4_Hkv1_S256_D128, +decode,1,2,4,1,512,128,0,18582,0.04,,99.6,5.306,32.423,analytic,2097152,528384,True,tb_logs/decode_gqa1_B2_Hq4_Hkv1_S512_D128, +decode,1,2,5,1,128,128,0,18806,0.01,,25.4,5.37,32.512,analytic,655360,136192,True,tb_logs/decode_gqa1_B2_Hq5_Hkv1_S128_D128, +decode,1,2,5,1,256,128,0,19595,0.03,,47.8,5.5953,35.616,analytic,1310720,267264,True,tb_logs/decode_gqa1_B2_Hq5_Hkv1_S256_D128, +decode,1,2,5,1,512,128,0,21703,0.05,,85.4,6.1974,36.802,analytic,2621440,529408,True,tb_logs/decode_gqa1_B2_Hq5_Hkv1_S512_D128, +decode,1,2,8,1,128,128,0,26438,0.02,,18.4,7.5494,32.421,analytic,1048576,139264,True,tb_logs/decode_gqa1_B2_Hq8_Hkv1_S128_D128, +decode,1,2,8,1,256,128,0,27663,0.03,,34.2,7.8993,32.271,analytic,2097152,270336,True,tb_logs/decode_gqa1_B2_Hq8_Hkv1_S256_D128, +decode,1,2,8,1,512,128,0,30680,0.05,,60.8,8.7608,40.5,analytic,4194304,532480,True,tb_logs/decode_gqa1_B2_Hq8_Hkv1_S512_D128, +decode,1,2,16,1,128,128,0,47095,0.02,,11.0,13.4482,39.148,analytic,2097152,147456,True,tb_logs/decode_gqa1_B2_Hq16_Hkv1_S128_D128, +decode,1,2,16,1,256,128,0,48884,0.03,,20.0,13.9588,47.974,analytic,4194304,278528,True,tb_logs/decode_gqa1_B2_Hq16_Hkv1_S256_D128, +decode,1,2,16,1,512,128,0,55037,0.06,,34.4,15.7159,47.914,analytic,8388608,540672,True,tb_logs/decode_gqa1_B2_Hq16_Hkv1_S512_D128, +decode,1,2,16,2,128,128,0,49294,0.02,,19.8,14.0761,48.709,analytic,2097152,278528,True,tb_logs/decode_gqa1_B2_Hq16_Hkv2_S128_D128, +decode,1,2,16,2,256,128,0,51404,0.03,,36.8,14.6784,34.378,analytic,4194304,540672,True,tb_logs/decode_gqa1_B2_Hq16_Hkv2_S256_D128, +decode,1,2,16,2,512,128,0,57268,0.06,,65.1,16.3529,34.268,analytic,8388608,1064960,True,tb_logs/decode_gqa1_B2_Hq16_Hkv2_S512_D128, +decode,1,2,12,4,128,128,0,43452,0.01,,43.2,12.4078,33.306,analytic,1572864,536576,True,tb_logs/decode_gqa1_B2_Hq12_Hkv4_S128_D128, +decode,1,2,12,4,256,128,0,45611,0.03,,81.5,13.0241,52.727,analytic,3145728,1060864,True,tb_logs/decode_gqa1_B2_Hq12_Hkv4_S256_D128, +decode,1,2,12,4,512,128,0,50924,0.05,,145.1,14.5414,53.209,analytic,6291456,2109440,True,tb_logs/decode_gqa1_B2_Hq12_Hkv4_S512_D128, +decode,1,2,32,4,128,128,0,94771,0.02,,20.6,27.0618,53.884,analytic,4194304,557056,True,tb_logs/decode_gqa1_B2_Hq32_Hkv4_S128_D128, +decode,1,2,32,4,256,128,0,99071,0.03,,38.2,28.2899,47.74,analytic,8388608,1081344,True,tb_logs/decode_gqa1_B2_Hq32_Hkv4_S256_D128, +decode,1,2,32,4,512,128,0,110348,0.06,,67.6,31.51,48.509,analytic,16777216,2129920,True,tb_logs/decode_gqa1_B2_Hq32_Hkv4_S512_D128, +decode,1,2,64,8,128,128,0,185796,0.02,,21.0,53.0543,58.56,analytic,8388608,1114112,True,tb_logs/decode_gqa1_B2_Hq64_Hkv8_S128_D128, +decode,1,2,64,8,256,128,0,194567,0.03,,38.9,55.5587,60.979,analytic,16777216,2162688,True,tb_logs/decode_gqa1_B2_Hq64_Hkv8_S256_D128, +decode,1,2,64,8,512,128,0,219574,0.06,,67.9,62.6997,67.966,analytic,33554432,4259840,True,tb_logs/decode_gqa1_B2_Hq64_Hkv8_S512_D128, +decode,1,4,2,1,128,128,0,18438,0.01,,50.6,5.265,52.996,analytic,524288,266240,True,tb_logs/decode_gqa1_B4_Hq2_Hkv1_S128_D128, +decode,1,4,2,1,256,128,0,19439,0.02,,95.2,5.5507,47.937,analytic,1048576,528384,True,tb_logs/decode_gqa1_B4_Hq2_Hkv1_S256_D128, +decode,1,4,2,1,512,128,0,21593,0.04,,170.7,6.166,46.175,analytic,2097152,1052672,True,tb_logs/decode_gqa1_B4_Hq2_Hkv1_S512_D128, +decode,1,4,4,1,128,128,0,29024,0.01,,32.6,8.2877,46.156,analytic,1048576,270336,True,tb_logs/decode_gqa1_B4_Hq4_Hkv1_S128_D128, +decode,1,4,4,1,256,128,0,30509,0.03,,61.1,8.712,46.456,analytic,2097152,532480,True,tb_logs/decode_gqa1_B4_Hq4_Hkv1_S256_D128, +decode,1,4,4,1,512,128,0,33771,0.05,,109.6,9.6435,47.034,analytic,4194304,1056768,True,tb_logs/decode_gqa1_B4_Hq4_Hkv1_S512_D128, +decode,1,4,5,1,128,128,0,34300,0.01,,27.8,9.7944,47.024,analytic,1310720,272384,True,tb_logs/decode_gqa1_B4_Hq5_Hkv1_S128_D128, +decode,1,4,5,1,256,128,0,35849,0.03,,52.2,10.2366,46.694,analytic,2621440,534528,True,tb_logs/decode_gqa1_B4_Hq5_Hkv1_S256_D128, +decode,1,4,5,1,512,128,0,39367,0.05,,94.2,11.2413,44.256,analytic,5242880,1058816,True,tb_logs/decode_gqa1_B4_Hq5_Hkv1_S512_D128, +decode,1,4,8,1,128,128,0,49643,0.02,,19.6,14.1757,41.928,analytic,2097152,278528,True,tb_logs/decode_gqa1_B4_Hq8_Hkv1_S128_D128, +decode,1,4,8,1,256,128,0,51873,0.03,,36.5,14.8125,49.319,analytic,4194304,540672,True,tb_logs/decode_gqa1_B4_Hq8_Hkv1_S256_D128, +decode,1,4,8,1,512,128,0,57696,0.06,,64.6,16.4752,42.607,analytic,8388608,1064960,True,tb_logs/decode_gqa1_B4_Hq8_Hkv1_S512_D128, +decode,1,4,16,1,128,128,0,90801,0.02,,11.4,25.9285,33.42,analytic,4194304,294912,True,tb_logs/decode_gqa1_B4_Hq16_Hkv1_S128_D128, +decode,1,4,16,1,256,128,0,94621,0.03,,20.6,27.0192,35.177,analytic,8388608,557056,True,tb_logs/decode_gqa1_B4_Hq16_Hkv1_S256_D128, +decode,1,4,16,1,512,128,0,105748,0.06,,35.8,30.1965,36.652,analytic,16777216,1081344,True,tb_logs/decode_gqa1_B4_Hq16_Hkv1_S512_D128, +decode,1,4,16,2,128,128,0,95274,0.02,,20.5,27.2055,48.915,analytic,4194304,557056,True,tb_logs/decode_gqa1_B4_Hq16_Hkv2_S128_D128, +decode,1,4,16,2,256,128,0,99529,0.03,,38.0,28.4207,49.201,analytic,8388608,1081344,True,tb_logs/decode_gqa1_B4_Hq16_Hkv2_S256_D128, +decode,1,4,16,2,512,128,0,111829,0.06,,66.7,31.9329,48.292,analytic,16777216,2129920,True,tb_logs/decode_gqa1_B4_Hq16_Hkv2_S512_D128, +decode,1,4,12,4,128,128,0,82894,0.01,,45.3,23.6706,48.489,analytic,3145728,1073152,True,tb_logs/decode_gqa1_B4_Hq12_Hkv4_S128_D128, +decode,1,4,12,4,256,128,0,87953,0.03,,84.5,25.115,47.426,analytic,6291456,2121728,True,tb_logs/decode_gqa1_B4_Hq12_Hkv4_S256_D128, +decode,1,4,12,4,512,128,0,97248,0.05,,151.9,27.7691,46.488,analytic,12582912,4218880,True,tb_logs/decode_gqa1_B4_Hq12_Hkv4_S512_D128, +decode,1,4,32,4,128,128,0,185859,0.02,,21.0,53.0723,58.8,analytic,8388608,1114112,True,tb_logs/decode_gqa1_B4_Hq32_Hkv4_S128_D128, +decode,1,4,32,4,256,128,0,194726,0.03,,38.9,55.6041,61.174,analytic,16777216,2162688,True,tb_logs/decode_gqa1_B4_Hq32_Hkv4_S256_D128, +decode,1,4,32,4,512,128,0,219579,0.06,,67.9,62.7012,68.086,analytic,33554432,4259840,True,tb_logs/decode_gqa1_B4_Hq32_Hkv4_S512_D128, +decode,1,4,64,8,128,128,0,367080,0.02,,21.3,104.8201,110.003,analytic,16777216,2228224,True,tb_logs/decode_gqa1_B4_Hq64_Hkv8_S128_D128, +decode,1,4,64,8,256,128,0,383560,0.03,,39.5,109.5261,114.696,analytic,33554432,4325376,True,tb_logs/decode_gqa1_B4_Hq64_Hkv8_S256_D128, +decode,1,4,64,8,512,128,0,437717,0.06,,68.2,124.9904,130.045,analytic,67108864,8519680,True,tb_logs/decode_gqa1_B4_Hq64_Hkv8_S512_D128, +decode,1,8,2,1,128,128,0,33355,0.01,,55.9,9.5246,38.137,analytic,1048576,532480,True,tb_logs/decode_gqa1_B8_Hq2_Hkv1_S128_D128, +decode,1,8,2,1,256,128,0,35565,0.02,,104.1,10.1557,46.841,analytic,2097152,1056768,True,tb_logs/decode_gqa1_B8_Hq2_Hkv1_S256_D128, +decode,1,8,2,1,512,128,0,39260,0.04,,187.8,11.2108,46.917,analytic,4194304,2105344,True,tb_logs/decode_gqa1_B8_Hq2_Hkv1_S512_D128, +decode,1,8,4,1,128,128,0,54652,0.01,,34.6,15.6061,46.656,analytic,2097152,540672,True,tb_logs/decode_gqa1_B8_Hq4_Hkv1_S128_D128, +decode,1,8,4,1,256,128,0,57435,0.03,,64.9,16.4005,46.757,analytic,4194304,1064960,True,tb_logs/decode_gqa1_B8_Hq4_Hkv1_S256_D128, +decode,1,8,4,1,512,128,0,63809,0.05,,116.0,18.2208,47.201,analytic,8388608,2113536,True,tb_logs/decode_gqa1_B8_Hq4_Hkv1_S512_D128, +decode,1,8,5,1,128,128,0,65172,0.02,,29.3,18.61,46.954,analytic,2621440,544768,True,tb_logs/decode_gqa1_B8_Hq5_Hkv1_S128_D128, +decode,1,8,5,1,256,128,0,68253,0.03,,54.9,19.4897,46.858,analytic,5242880,1069056,True,tb_logs/decode_gqa1_B8_Hq5_Hkv1_S256_D128, +decode,1,8,5,1,512,128,0,75918,0.05,,97.7,21.6784,47.909,analytic,10485760,2117632,True,tb_logs/decode_gqa1_B8_Hq5_Hkv1_S512_D128, +decode,1,8,8,1,128,128,0,96172,0.02,,20.3,27.4621,48.808,analytic,4194304,557056,True,tb_logs/decode_gqa1_B8_Hq8_Hkv1_S128_D128, +decode,1,8,8,1,256,128,0,100243,0.03,,37.8,28.6246,48.168,analytic,8388608,1081344,True,tb_logs/decode_gqa1_B8_Hq8_Hkv1_S256_D128, +decode,1,8,8,1,512,128,0,111697,0.06,,66.8,31.8951,48.631,analytic,16777216,2129920,True,tb_logs/decode_gqa1_B8_Hq8_Hkv1_S512_D128, +decode,1,8,16,1,128,128,0,178364,0.02,,11.6,50.932,56.263,analytic,8388608,589824,True,tb_logs/decode_gqa1_B8_Hq16_Hkv1_S128_D128, +decode,1,8,16,1,256,128,0,185921,0.03,,21.0,53.09,58.608,analytic,16777216,1114112,True,tb_logs/decode_gqa1_B8_Hq16_Hkv1_S256_D128, +decode,1,8,16,1,512,128,0,208234,0.06,,36.4,59.4616,65.056,analytic,33554432,2162688,True,tb_logs/decode_gqa1_B8_Hq16_Hkv1_S512_D128, +decode,1,8,16,2,128,128,0,187220,0.02,,20.8,53.461,59.068,analytic,8388608,1114112,True,tb_logs/decode_gqa1_B8_Hq16_Hkv2_S128_D128, +decode,1,8,16,2,256,128,0,195597,0.03,,38.7,55.8528,61.405,analytic,16777216,2162688,True,tb_logs/decode_gqa1_B8_Hq16_Hkv2_S256_D128, +decode,1,8,16,2,512,128,0,219590,0.06,,67.9,62.7041,68.351,analytic,33554432,4259840,True,tb_logs/decode_gqa1_B8_Hq16_Hkv2_S512_D128, +decode,1,8,12,4,128,128,0,162852,0.01,,46.2,46.5027,51.971,analytic,6291456,2146304,True,tb_logs/decode_gqa1_B8_Hq12_Hkv4_S128_D128, +decode,1,8,12,4,256,128,0,172492,0.03,,86.2,49.2552,54.698,analytic,12582912,4243456,True,tb_logs/decode_gqa1_B8_Hq12_Hkv4_S256_D128, +decode,1,8,12,4,512,128,0,192073,0.05,,153.8,54.8467,60.099,analytic,25165824,8437760,True,tb_logs/decode_gqa1_B8_Hq12_Hkv4_S512_D128, +decode,1,8,32,4,128,128,0,368743,0.02,,21.2,105.2951,110.415,analytic,16777216,2228224,True,tb_logs/decode_gqa1_B8_Hq32_Hkv4_S128_D128, +decode,1,8,32,4,256,128,0,386003,0.03,,39.2,110.2235,115.412,analytic,33554432,4325376,True,tb_logs/decode_gqa1_B8_Hq32_Hkv4_S256_D128, +decode,1,8,32,4,512,128,0,437817,0.06,,68.1,125.0192,130.027,analytic,67108864,8519680,True,tb_logs/decode_gqa1_B8_Hq32_Hkv4_S512_D128, +decode,1,8,64,8,128,128,0,731046,0.02,,21.3,208.7509,214.123,analytic,33554432,4456448,True,tb_logs/decode_gqa1_B8_Hq64_Hkv8_S128_D128, +decode,1,8,64,8,256,128,0,765875,0.03,,39.6,218.6965,224.308,analytic,67108864,8650752,True,tb_logs/decode_gqa1_B8_Hq64_Hkv8_S256_D128, +decode,1,8,64,8,512,128,0,875185,0.06,,68.2,249.9101,253.756,analytic,134217728,17039360,True,tb_logs/decode_gqa1_B8_Hq64_Hkv8_S512_D128, +decode,1,16,2,1,128,128,0,63332,0.01,,58.9,18.0845,47.914,analytic,2097152,1064960,True,tb_logs/decode_gqa1_B16_Hq2_Hkv1_S128_D128, +decode,1,16,2,1,256,128,0,67385,0.02,,109.8,19.242,48.256,analytic,4194304,2113536,True,tb_logs/decode_gqa1_B16_Hq2_Hkv1_S256_D128, +decode,1,16,2,1,512,128,0,74712,0.04,,197.4,21.3342,47.661,analytic,8388608,4210688,True,tb_logs/decode_gqa1_B16_Hq2_Hkv1_S512_D128, +decode,1,16,4,1,128,128,0,105993,0.02,,35.7,30.2663,48.197,analytic,4194304,1081344,True,tb_logs/decode_gqa1_B16_Hq4_Hkv1_S128_D128, +decode,1,16,4,1,256,128,0,111076,0.03,,67.2,31.7179,36.555,analytic,8388608,2129920,True,tb_logs/decode_gqa1_B16_Hq4_Hkv1_S256_D128, +decode,1,16,4,1,512,128,0,123078,0.05,,120.3,35.1449,39.758,analytic,16777216,4227072,True,tb_logs/decode_gqa1_B16_Hq4_Hkv1_S512_D128, +decode,1,16,5,1,128,128,0,126696,0.02,,30.1,36.1782,40.829,analytic,5242880,1089536,True,tb_logs/decode_gqa1_B16_Hq5_Hkv1_S128_D128, +decode,1,16,5,1,256,128,0,132920,0.03,,56.3,37.9556,42.611,analytic,10485760,2138112,True,tb_logs/decode_gqa1_B16_Hq5_Hkv1_S256_D128, +decode,1,16,5,1,512,128,0,146926,0.05,,100.9,41.9548,46.84,analytic,20971520,4235264,True,tb_logs/decode_gqa1_B16_Hq5_Hkv1_S512_D128, +decode,1,16,8,1,128,128,0,188027,0.02,,20.8,53.6914,58.279,analytic,8388608,1114112,True,tb_logs/decode_gqa1_B16_Hq8_Hkv1_S128_D128, +decode,1,16,8,1,256,128,0,196835,0.03,,38.5,56.2066,60.888,analytic,16777216,2162688,True,tb_logs/decode_gqa1_B16_Hq8_Hkv1_S256_D128, +decode,1,16,8,1,512,128,0,219505,0.06,,68.0,62.6798,67.464,analytic,33554432,4259840,True,tb_logs/decode_gqa1_B16_Hq8_Hkv1_S512_D128, +decode,1,16,16,1,128,128,0,351710,0.02,,11.7,100.4311,105.185,analytic,16777216,1179648,True,tb_logs/decode_gqa1_B16_Hq16_Hkv1_S128_D128, +decode,1,16,16,1,256,128,0,367074,0.03,,21.3,104.8184,109.507,analytic,33554432,2228224,True,tb_logs/decode_gqa1_B16_Hq16_Hkv1_S256_D128, +decode,1,16,16,1,512,128,0,412688,0.06,,36.7,117.8435,122.658,analytic,67108864,4325376,True,tb_logs/decode_gqa1_B16_Hq16_Hkv1_S512_D128, +decode,1,16,16,2,128,128,0,369201,0.02,,21.1,105.4256,110.132,analytic,16777216,2228224,True,tb_logs/decode_gqa1_B16_Hq16_Hkv2_S128_D128, +decode,1,16,16,2,256,128,0,387437,0.03,,39.1,110.6331,115.457,analytic,33554432,4325376,True,tb_logs/decode_gqa1_B16_Hq16_Hkv2_S256_D128, +decode,1,16,16,2,512,128,0,437950,0.06,,68.1,125.057,129.482,analytic,67108864,8519680,True,tb_logs/decode_gqa1_B16_Hq16_Hkv2_S512_D128, +decode,1,16,12,4,128,128,0,321426,0.01,,46.8,91.7835,96.43,analytic,12582912,4292608,True,tb_logs/decode_gqa1_B16_Hq12_Hkv4_S128_D128, +decode,1,16,12,4,256,128,0,340588,0.03,,87.3,97.2552,101.996,analytic,25165824,8486912,True,tb_logs/decode_gqa1_B16_Hq12_Hkv4_S256_D128, +decode,1,16,12,4,512,128,0,380726,0.05,,155.2,108.7167,113.437,analytic,50331648,16875520,True,tb_logs/decode_gqa1_B16_Hq12_Hkv4_S512_D128, +decode,1,16,32,4,128,128,0,733511,0.02,,21.3,209.4549,214.437,analytic,33554432,4456448,True,tb_logs/decode_gqa1_B16_Hq32_Hkv4_S128_D128, +decode,1,16,32,4,256,128,0,768295,0.03,,39.4,219.3875,225.119,analytic,67108864,8650752,True,tb_logs/decode_gqa1_B16_Hq32_Hkv4_S256_D128, +decode,1,16,32,4,512,128,0,875161,0.06,,68.2,249.9031,254.128,analytic,134217728,17039360,True,tb_logs/decode_gqa1_B16_Hq32_Hkv4_S512_D128, +decode,1,16,64,8,128,128,0,1462166,0.02,,21.3,417.5232,423.321,analytic,67108864,8912896,True,tb_logs/decode_gqa1_B16_Hq64_Hkv8_S128_D128, +decode,1,16,64,8,256,128,0,1529391,0.03,,39.6,436.7192,442.248,analytic,134217728,17301504,True,tb_logs/decode_gqa1_B16_Hq64_Hkv8_S256_D128, +decode,1,16,64,8,512,128,0,1749295,0.06,,68.2,499.513,506.862,analytic,268435456,34078720,True,tb_logs/decode_gqa1_B16_Hq64_Hkv8_S512_D128, +decode,1,32,2,1,128,128,0,123020,0.01,,60.6,35.1285,39.734,analytic,4194304,2129920,True,tb_logs/decode_gqa1_B32_Hq2_Hkv1_S128_D128, +decode,1,32,2,1,256,128,0,130912,0.02,,113.1,37.382,41.997,analytic,8388608,4227072,True,tb_logs/decode_gqa1_B32_Hq2_Hkv1_S256_D128, +decode,1,32,2,1,512,128,0,146483,0.04,,201.3,41.8285,46.693,analytic,16777216,8421376,True,tb_logs/decode_gqa1_B32_Hq2_Hkv1_S512_D128, +decode,1,32,4,1,128,128,0,208541,0.02,,36.3,59.5491,64.186,analytic,8388608,2162688,True,tb_logs/decode_gqa1_B32_Hq4_Hkv1_S128_D128, +decode,1,32,4,1,256,128,0,219234,0.03,,68.0,62.6024,67.122,analytic,16777216,4259840,True,tb_logs/decode_gqa1_B32_Hq4_Hkv1_S256_D128, +decode,1,32,4,1,512,128,0,242500,0.05,,122.1,69.2461,73.844,analytic,33554432,8454144,True,tb_logs/decode_gqa1_B32_Hq4_Hkv1_S512_D128, +decode,1,32,5,1,128,128,0,249986,0.02,,30.5,71.3837,76.037,analytic,10485760,2179072,True,tb_logs/decode_gqa1_B32_Hq5_Hkv1_S128_D128, +decode,1,32,5,1,256,128,0,262677,0.03,,57.0,75.0078,79.788,analytic,20971520,4276224,True,tb_logs/decode_gqa1_B32_Hq5_Hkv1_S256_D128, +decode,1,32,5,1,512,128,0,291280,0.05,,101.8,83.1754,87.88,analytic,41943040,8470528,True,tb_logs/decode_gqa1_B32_Hq5_Hkv1_S512_D128, +decode,1,32,8,1,128,128,0,373622,0.02,,20.9,106.6882,111.368,analytic,16777216,2228224,True,tb_logs/decode_gqa1_B32_Hq8_Hkv1_S128_D128, +decode,1,32,8,1,256,128,0,390147,0.03,,38.8,111.407,116.121,analytic,33554432,4325376,True,tb_logs/decode_gqa1_B32_Hq8_Hkv1_S256_D128, +decode,1,32,8,1,512,128,0,438089,0.06,,68.1,125.0967,129.528,analytic,67108864,8519680,True,tb_logs/decode_gqa1_B32_Hq8_Hkv1_S512_D128, +decode,1,32,16,1,128,128,0,701999,0.02,,11.8,200.4565,205.479,analytic,33554432,2359296,True,tb_logs/decode_gqa1_B32_Hq16_Hkv1_S128_D128, +decode,1,32,16,1,256,128,0,731465,0.03,,21.3,208.8705,213.714,analytic,67108864,4456448,True,tb_logs/decode_gqa1_B32_Hq16_Hkv1_S256_D128, +decode,1,32,16,1,512,128,0,820553,0.06,,36.9,234.3099,239.42,analytic,134217728,8650752,True,tb_logs/decode_gqa1_B32_Hq16_Hkv1_S512_D128, +decode,1,32,16,2,128,128,0,736133,0.02,,21.2,210.2037,215.202,analytic,33554432,4456448,True,tb_logs/decode_gqa1_B32_Hq16_Hkv2_S128_D128, +decode,1,32,16,2,256,128,0,771063,0.03,,39.3,220.178,225.158,analytic,67108864,8650752,True,tb_logs/decode_gqa1_B32_Hq16_Hkv2_S256_D128, +decode,1,32,16,2,512,128,0,875174,0.06,,68.2,249.907,254.29,analytic,134217728,17039360,True,tb_logs/decode_gqa1_B32_Hq16_Hkv2_S512_D128, +decode,1,32,12,4,128,128,0,639796,0.02,,47.0,182.6944,188.13,analytic,25165824,8585216,True,tb_logs/decode_gqa1_B32_Hq12_Hkv4_S128_D128, +decode,1,32,12,4,256,128,0,679306,0.03,,87.5,193.9767,199.358,analytic,50331648,16973824,True,tb_logs/decode_gqa1_B32_Hq12_Hkv4_S256_D128, +decode,1,32,12,4,512,128,0,755580,0.05,,156.4,215.7567,221.244,analytic,100663296,33751040,True,tb_logs/decode_gqa1_B32_Hq12_Hkv4_S512_D128, +decode,1,32,32,4,128,128,0,1464008,0.02,,21.3,418.0491,423.93,analytic,67108864,8912896,True,tb_logs/decode_gqa1_B32_Hq32_Hkv4_S128_D128, +decode,1,32,32,4,256,128,0,1554522,0.03,,39.0,443.8954,450.034,analytic,134217728,17301504,True,tb_logs/decode_gqa1_B32_Hq32_Hkv4_S256_D128, +decode,1,32,32,4,512,128,0,1749324,0.06,,68.2,499.5213,506.836,analytic,268435456,34078720,True,tb_logs/decode_gqa1_B32_Hq32_Hkv4_S512_D128, +decode,1,32,64,8,128,128,0,2949199,0.02,,21.2,842.147,848.657,analytic,134217728,17825792,True,tb_logs/decode_gqa1_B32_Hq64_Hkv8_S128_D128, +decode,1,32,64,8,256,128,0,3052177,0.03,,39.7,871.5525,878.025,analytic,268435456,34603008,True,tb_logs/decode_gqa1_B32_Hq64_Hkv8_S256_D128, +decode,1,32,64,8,512,128,0,3497547,0.06,,68.2,998.7285,1006.551,analytic,536870912,68157440,True,tb_logs/decode_gqa1_B32_Hq64_Hkv8_S512_D128, diff --git a/tests/ops/attention/__init__.py b/tests/ops/attention/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/ops/attention/test_gqa.py b/tests/ops/attention/test_gqa.py deleted file mode 100644 index 038861c8..00000000 --- a/tests/ops/attention/test_gqa.py +++ /dev/null @@ -1,319 +0,0 @@ -import sys -import os -import torch -import torch.nn as nn -import torch.nn.functional as F -import torch._dynamo -import argparse -sys.path.insert(0, os.path.join(os.environ.get('TORCHSIM_DIR', default='/workspace/PyTorchSim'), 'tests')) -from _pytorchsim_utils import test_result - - - -class GQAMultiheadAttention(nn.Module): - """ - Grouped Query Attention (GQA) implementation. - Query has num_heads, but key/value have num_kv_heads (num_kv_heads < num_heads). - """ - def __init__(self, embed_dim, num_heads, num_kv_heads=None, head_dim=None, bias=True, dropout=0.0): - super().__init__() - assert embed_dim % num_heads == 0 - if head_dim is None: - head_dim = embed_dim // num_heads - assert embed_dim == num_heads * head_dim - - # If num_kv_heads is not specified, use num_heads (standard MHA) - if num_kv_heads is None: - num_kv_heads = num_heads - - assert num_kv_heads <= num_heads - assert embed_dim % num_kv_heads == 0 - - self.embed_dim = embed_dim - self.num_heads = num_heads - self.num_kv_heads = num_kv_heads - self.head_dim = head_dim - self.dropout = dropout - - # QKV projection: Q has embed_dim, K and V have kv_embed_dim each - kv_embed_dim = num_kv_heads * head_dim - total_qkv_dim = embed_dim + 2 * kv_embed_dim - - self.qkv_proj = nn.Linear(embed_dim, total_qkv_dim, bias=bias) - self.out_proj = nn.Linear(embed_dim, embed_dim, bias=bias) - - def forward(self, query, key=None, value=None, attn_mask=None, need_weights=False): - """ - Args: - query: [batch, seq_len, embed_dim] or [seq_len, batch, embed_dim] - key: optional, same shape as query - value: optional, same shape as query - attn_mask: optional attention mask - need_weights: whether to return attention weights - """ - # For compatibility with nn.MultiheadAttention API - if key is None: - key = query - if value is None: - value = query - - # Handle batch_first vs batch_second - if query.dim() == 3: - batch_first = True - batch_size, seq_len, _ = query.shape - else: - batch_first = False - seq_len, batch_size, _ = query.shape - query = query.transpose(0, 1) - key = key.transpose(0, 1) - value = value.transpose(0, 1) - - # Project QKV - # Use query for QKV projection (standard MHA/GQA pattern) - qkv = self.qkv_proj(query) # [batch, seq_len, total_qkv_dim] - - # Split into Q, K, V - kv_embed_dim = self.num_kv_heads * self.head_dim - q = qkv[:, :, :self.embed_dim] # [batch, seq_len, embed_dim] - k = qkv[:, :, self.embed_dim:self.embed_dim + kv_embed_dim] # [batch, seq_len, kv_embed_dim] - v = qkv[:, :, self.embed_dim + kv_embed_dim:] # [batch, seq_len, kv_embed_dim] - - # Reshape to multi-head format - q = q.view(batch_size, seq_len, self.num_heads, self.head_dim) # [batch, seq_len, num_heads, head_dim] - k = k.view(batch_size, seq_len, self.num_kv_heads, self.head_dim) # [batch, seq_len, num_kv_heads, head_dim] - v = v.view(batch_size, seq_len, self.num_kv_heads, self.head_dim) # [batch, seq_len, num_kv_heads, head_dim] - - # Transpose for attention: [batch, num_heads, seq_len, head_dim] - q = q.transpose(1, 2) # [batch, num_heads, seq_len, head_dim] - k = k.transpose(1, 2) # [batch, num_kv_heads, seq_len, head_dim] - v = v.transpose(1, 2) # [batch, num_kv_heads, seq_len, head_dim] - - # Scaled dot product attention with GQA support - # enable_gqa=True allows different number of heads for Q vs K/V - attn_output = F.scaled_dot_product_attention( - q, k, v, - attn_mask=attn_mask, - dropout_p=self.dropout if self.training else 0.0, - is_causal=False, - enable_gqa=(self.num_kv_heads < self.num_heads) - ) # [batch, num_heads, seq_len, head_dim] - - # Reshape back: [batch, num_heads, seq_len, head_dim] -> [batch, seq_len, embed_dim] - attn_output = attn_output.transpose(1, 2) # [batch, seq_len, num_heads, head_dim] - attn_output = attn_output.contiguous().view(batch_size, seq_len, self.embed_dim) - - # Output projection - output = self.out_proj(attn_output) # [batch, seq_len, embed_dim] - - if not batch_first: - output = output.transpose(0, 1) # [seq_len, batch, embed_dim] - - if need_weights: - # Compute attention weights for return - # This is simplified - in practice you'd want the actual attention weights - attn_weights = None - return output, attn_weights - else: - return output - - -def test_gqa_attention(device, batch=1, seq_len=32, embed_dim=768, num_heads=12, num_kv_heads=4): - """ - Test Grouped Query Attention (GQA) where num_kv_heads < num_heads. - - Args: - device: target device - batch: batch size - seq_len: sequence length - embed_dim: embedding dimension - num_heads: number of query heads - num_kv_heads: number of key/value heads (should be <= num_heads) - """ - print(f"Testing GQA Attention (batch={batch}, seq_len={seq_len}, embed_dim={embed_dim}, " - f"num_heads={num_heads}, num_kv_heads={num_kv_heads})") - - # Create GQA model - gqa = GQAMultiheadAttention( - embed_dim=embed_dim, - num_heads=num_heads, - num_kv_heads=num_kv_heads, - bias=True, - dropout=0.0 - ).eval() - - # Initialize weights - torch.nn.init.normal_(gqa.qkv_proj.weight, mean=0.0, std=0.02) - torch.nn.init.normal_(gqa.qkv_proj.bias, mean=0.0, std=0.02) - torch.nn.init.normal_(gqa.out_proj.weight, mean=0.0, std=0.02) - torch.nn.init.normal_(gqa.out_proj.bias, mean=0.0, std=0.02) - - # Create input - x = torch.randn(batch, seq_len, embed_dim) - query = x.clone() - key = x.clone() - value = x.clone() - - # Run on custom device - gqa_device = gqa.to(device) - q1, k1, v1 = query.to(device), key.to(device), value.to(device) - - compiled_gqa = torch.compile(gqa_device, dynamic=False) - with torch.no_grad(): - out_device = compiled_gqa(q1, k1, v1) - - # Run on CPU - gqa_cpu = gqa.cpu() - q2, k2, v2 = query.cpu(), key.cpu(), value.cpu() - with torch.no_grad(): - out_cpu = gqa_cpu(q2, k2, v2) - - test_result("GQA Attention", out_device, out_cpu) - print("Max diff > ", torch.max(torch.abs(out_device.cpu() - out_cpu))) - print("GQA Attention Simulation Done") - - -def test_standard_mha_via_gqa(device, batch=1, seq_len=32, embed_dim=768, num_heads=12): - """ - Test standard Multi-Head Attention using GQA with num_kv_heads == num_heads. - This should behave the same as standard MHA. - """ - print(f"Testing Standard MHA via GQA (batch={batch}, seq_len={seq_len}, " - f"embed_dim={embed_dim}, num_heads={num_heads})") - - test_gqa_attention(device, batch, seq_len, embed_dim, num_heads, num_kv_heads=num_heads) - - -def test_repeat_interleave_compilation(device, batch=1, seq_len=32, embed_dim=768, num_heads=12, num_kv_heads=4): - """ - Test that repeat_interleave operation compiles and works correctly using scaled_dot_product_attention implementation. - - This test uses the exact implementation from F.scaled_dot_product_attention to verify - that repeat_interleave works correctly when enable_gqa=True. - - Args: - device: target device - batch: batch size - seq_len: sequence length - embed_dim: embedding dimension - num_heads: number of query heads - num_kv_heads: number of key/value heads (should be < num_heads) - """ - import math - - print(f"Testing repeat_interleave compilation using scaled_dot_product_attention implementation " - f"(batch={batch}, seq_len={seq_len}, embed_dim={embed_dim}, " - f"num_heads={num_heads}, num_kv_heads={num_kv_heads})") - - head_dim = embed_dim // num_heads - assert num_kv_heads < num_heads, "num_kv_heads must be less than num_heads for GQA" - - # Create Q, K, V tensors - # Q: [batch, num_heads, seq_len, head_dim] - # K, V: [batch, num_kv_heads, seq_len, head_dim] - q = torch.randn(batch, num_heads, seq_len, head_dim) - k = torch.randn(batch, num_kv_heads, seq_len, head_dim) - v = torch.randn(batch, num_kv_heads, seq_len, head_dim) - - # Move to device - q_device = q.to(device) - k_device = k.to(device) - v_device = v.to(device) - - # Implementation from F.scaled_dot_product_attention - def scaled_dot_product_attention(query, key, value, attn_mask=None, dropout_p=0.0, - is_causal=False, scale=None, enable_gqa=False) -> torch.Tensor: - L, S = query.size(-2), key.size(-2) - scale_factor = 1 / math.sqrt(query.size(-1)) if scale is None else scale - attn_bias = torch.zeros(L, S, dtype=query.dtype, device=query.device) - if is_causal: - assert attn_mask is None - temp_mask = torch.ones(L, S, dtype=torch.bool).tril(diagonal=0) - attn_bias.masked_fill_(temp_mask.logical_not(), float("-inf")) - attn_bias.to(query.dtype) - - if attn_mask is not None: - if attn_mask.dtype == torch.bool: - attn_bias.masked_fill_(attn_mask.logical_not(), float("-inf")) - else: - attn_bias = attn_mask + attn_bias - - if enable_gqa: - key = key.repeat_interleave(query.size(-3)//key.size(-3), -3) - value = value.repeat_interleave(query.size(-3)//value.size(-3), -3) - - attn_weight = query @ key.transpose(-2, -1) * scale_factor - attn_weight += attn_bias - attn_weight = torch.softmax(attn_weight, dim=-1) - return attn_weight, value, attn_weight @ value - - # Compile the function - compiled_attn = torch.compile(scaled_dot_product_attention, dynamic=False) - - # Run on custom device with enable_gqa=True - with torch.no_grad(): - output_device = compiled_attn(q_device, k_device, v_device, - attn_mask=None, dropout_p=0.0, - is_causal=False, scale=None, enable_gqa=True) - - # Run on CPU for comparison - q_cpu = q.cpu() - k_cpu = k.cpu() - v_cpu = v.cpu() - with torch.no_grad(): - output_cpu = scaled_dot_product_attention(q_cpu, k_cpu, v_cpu, - attn_mask=None, dropout_p=0.0, - is_causal=False, scale=None, enable_gqa=True) - - # Compare results - test_result("repeat_interleave in scaled_dot_product_attention", output_device[0], output_cpu[0]) - print("Max diff > ", torch.max(torch.abs(output_device[0].cpu() - output_cpu[0]))) - test_result("repeat_interleave in scaled_dot_product_attention", output_device[1], output_cpu[1]) - print("Max diff > ", torch.max(torch.abs(output_device[1].cpu() - output_cpu[1]))) - test_result("repeat_interleave in scaled_dot_product_attention", output_device[2], output_cpu[2]) - print("Max diff > ", torch.max(torch.abs(output_device[2].cpu() - output_cpu[2]))) - print("repeat_interleave compilation test Done") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--device", type=str, default="npu", help="Device to use") - parser.add_argument("--batch", type=int, default=1, help="Batch size") - parser.add_argument("--seq_len", type=int, default=32, help="Sequence length") - parser.add_argument("--embed_dim", type=int, default=768, help="Embedding dimension") - parser.add_argument("--num_heads", type=int, default=8, help="Number of query heads") - parser.add_argument("--num_kv_heads", type=int, default=4, help="Number of key/value heads") - parser.add_argument("--test_standard", action="store_true", help="Also test standard MHA via GQA") - parser.add_argument("--test_repeat_interleave", action="store_true", help="Test repeat_interleave compilation") - - args = parser.parse_args() - - device = torch.device("npu:0") - - test_repeat_interleave_compilation( - device=device, - batch=args.batch, - seq_len=args.seq_len, - embed_dim=args.embed_dim, - num_heads=args.num_heads, - num_kv_heads=args.num_kv_heads - ) - - # Test GQA - test_gqa_attention( - device=device, - batch=args.batch, - seq_len=args.seq_len, - embed_dim=args.embed_dim, - num_heads=args.num_heads, - num_kv_heads=args.num_kv_heads - ) - - # Optionally test standard MHA via GQA - # if args.test_standard: - # test_standard_mha_via_gqa( - # device=args.device, - # batch=args.batch, - # seq_len=args.seq_len, - # embed_dim=args.embed_dim, - # num_heads=args.num_heads - # ) diff --git a/tests/ops/attention/test_gqa_decode.py b/tests/ops/attention/test_gqa_decode.py deleted file mode 100644 index 7a7ab06c..00000000 --- a/tests/ops/attention/test_gqa_decode.py +++ /dev/null @@ -1,215 +0,0 @@ -import torch -import torch.nn as nn -import torch.nn.functional as F -import os -import sys -import math -import argparse -from Simulator.simulator import TOGSimulator -device = torch.device("npu:0") -# ───────────────────────────────────────────────────────────────────────────── -# Optimized: Flash-Decode style — tile S upfront, batch in B dimension -# ───────────────────────────────────────────────────────────────────────────── - -class GQADecodeOptimized(nn.Module): - """Flash-Decode style GQA decode for multi-core NPU. - - Splits the KV-cache sequence into n_tiles chunks and folds them into the - BMM batch dimension (B_total = H_kv × n_tiles). Both the QK and SV - matrix multiplications are issued as a *single* batched BMM with a short - inner-K loop, so the NPU scheduler can distribute all B_total tiles across - available cores simultaneously. - - Improvement over GQABaseline - ───────────────────────────── - Baseline QK : B=H_kv=1, M=G, N=S(large), K=D → 640 N-tile iters on 1 batch - Optimized QK: B=H_kv*n_tiles, M=G, N=T(small), K=D → n_tiles batch slots for cores - - Baseline SV : B=H_kv=1, M=G, N=D, K=S → K-loop=640, only 8 outer tiles - Optimized SV: B=H_kv*n_tiles, M=G, N=D, K=T → K-loop=T/TILE_K, n_tiles outer tiles - - Memory layout improvements - ────────────────────────── - • K/V tiles are generated with a single contiguous view+reshape (no mid-loop transpose). - • Avoids materializing the full score tensor [H_kv, G, S] in DRAM before tiling. - • Softmax intermediates are kept in smaller [B_total, G, T] buffers. - - Input conventions - ───────────────── - q : [H_kv, G, D] – one decode-step query token per KV head - k : [H_kv, S, D] – KV-cache keys (NOT pre-transposed) - v : [H_kv, S, D] – KV-cache values - - tile_size selection - ─────────────────── - Ideal: tile_size = round_up(S * H_kv / num_cores, vpu_num_lanes) - so that B_total ≈ num_cores. Must also satisfy the SPAD budget: - (G*T + T*D + G*D) * bytes ≤ spad_per_core (for sub-tile occupancy) - Default 512 works for (G=5, D=128, fp16, 16-lane × 8 KB/lane SPAD). - """ - - def __init__(self, tile_size: int = 512): - super().__init__() - self.tile_size = tile_size - - def forward( - self, - q: torch.Tensor, # [H_kv, G, D] - k: torch.Tensor, # [H_kv, S, D] - v: torch.Tensor, # [H_kv, S, D] - scale: float, - ) -> torch.Tensor: - H_kv, G, D = q.shape - _, S, _ = k.shape - T = self.tile_size - n_tiles = (S + T - 1) // T - pad_len = n_tiles * T - S - B_total = H_kv * n_tiles - - # ── 1. Pad S → multiple of T ─────────────────────────────────────── - if pad_len > 0: - k = F.pad(k, (0, 0, 0, pad_len)) # [H_kv, S', D] - v = F.pad(v, (0, 0, 0, pad_len)) # [H_kv, S', D] - - # ── 2. Tile K, V → [B_total, T, D] (contiguous, no copy) ───────── - # k is [H_kv, S', D]; view splits S' → n_tiles×T along dim-1 - k_tiles = k.view(H_kv, n_tiles, T, D).reshape(B_total, T, D) - v_tiles = v.view(H_kv, n_tiles, T, D).reshape(B_total, T, D) - - # ── 3. Expand Q → [B_total, G, D] ───────────────────────────────── - # expand: zero-copy view; reshape: contiguous copy (small: B_total*G*D elems) - q_exp = q.unsqueeze(1).expand(H_kv, n_tiles, G, D).reshape(B_total, G, D) - - # ── 4. Batched QK BMM ────────────────────────────────────────────── - # [B_total, G, D] × [B_total, D, T] → [B_total, G, T] - # NPU mapping: B=B_total, M=G, N=T, K=D - # → outer tiles = B_total × M_tiles × N_tiles (all parallelizable) - # → inner K-loop = D/TILE_K (short, D=128) - k_t = k_tiles.transpose(1, 2) # [B_total, D, T] - scores = torch.bmm(q_exp, k_t) * scale # [B_total, G, T] - - # ── 5. Tile-local softmax (fp32 accumulation) ────────────────────── - # All ops are elementwise on [B_total, G, T] → torch.compile fuses them - scores_f32 = scores.float() - local_max = scores_f32.amax(dim=-1, keepdim=True) # [B_total, G, 1] - local_exp = (scores_f32 - local_max).exp() # [B_total, G, T] - local_sum = local_exp.sum(dim=-1, keepdim=True) # [B_total, G, 1] - - # ── 6. Batched SV BMM ────────────────────────────────────────────── - # [B_total, G, T] × [B_total, T, D] → [B_total, G, D] - # NPU mapping: B=B_total, M=G, N=D, K=T - # → outer tiles = B_total × M_tiles × N_tiles (parallelizable) - # → inner K-loop = T/TILE_K (controlled, T≪S) - sv = torch.bmm(local_exp.to(q.dtype), v_tiles) # [B_total, G, D] - - # ── 7. Online-softmax global reduction (elementwise, fused) ──────── - local_max = local_max.view(H_kv, n_tiles, G, 1) - local_sum = local_sum.view(H_kv, n_tiles, G, 1) - sv = sv.view(H_kv, n_tiles, G, D) - - global_max = local_max.amax(dim=1, keepdim=True) # [H_kv, 1, G, 1] - rescale = (local_max - global_max).exp() # [H_kv, n_tiles, G, 1] - corrected_sv = (sv * rescale).sum(dim=1) # [H_kv, G, D] - corrected_sum = (local_sum * rescale).sum(dim=1) # [H_kv, G, 1] - - return (corrected_sv / corrected_sum.clamp_min(1e-12)).to(q.dtype) - - -# ───────────────────────────────────────────────────────────────────────────── -# Test -# ───────────────────────────────────────────────────────────────────────────── - -MODEL_CONFIGS = { - "LLAMA4_TP8": { - "HEAD_DIM": 128, - "NUM_HEADS": 5, # = 40 total / TP8 - "NUM_KV_HEADS": 1, # = 8 total / TP8 - }, - "QWEN3-235B_TP4": { - "HEAD_DIM": 128, - "NUM_HEADS": 16, - "NUM_KV_HEADS": 1, - }, - "GPT-OSS_TP1": { - "HEAD_DIM": 64, - "NUM_HEADS": 64, - "NUM_KV_HEADS": 8, - }, - "GPT-OSS_TP2": { - "HEAD_DIM": 64, - "NUM_HEADS": 32, - "NUM_KV_HEADS": 4, - }, - "GPT-OSS_TP4": { - "HEAD_DIM": 64, - "NUM_HEADS": 16, - "NUM_KV_HEADS": 2, - }, - "GPT-OSS_TP8": { - "HEAD_DIM": 64, - "NUM_HEADS": 8, - "NUM_KV_HEADS": 1, - }, -} - - -def _make_inputs(cfg, seq_len, dtype): - H_kv = cfg["NUM_KV_HEADS"] - G = cfg["NUM_HEADS"] // cfg["NUM_KV_HEADS"] - D = cfg["HEAD_DIM"] - scale = 1.0 / math.sqrt(D) - - q = torch.randn(H_kv, G, D, dtype=dtype) - k = torch.randn(H_kv, seq_len, D, dtype=dtype) # NOT pre-transposed - v = torch.randn(H_kv, seq_len, D, dtype=dtype) - return q, k, v, scale - - -def test_gqa_decode_optimized(model, device, seq_len: int = 10240, tile_size: int = 512): - - cfg = MODEL_CONFIGS[model] if model is not None else MODEL_CONFIGS["LLAMA4_TP8"] - dtype = torch.float16 - - model = GQADecodeOptimized(tile_size=tile_size).eval() - - # ── NPU run ──────────────────────────────────────────────────────────── - q, k, v, scale = _make_inputs(cfg, seq_len, dtype) - model_dev = model.to(device) - compiled = torch.compile(model_dev, dynamic=False) - - q_dev, k_dev, v_dev = q.to(device), k.to(device), v.to(device) - with torch.no_grad(): - with TOGSimulator(): - out_dev = compiled(q_dev, k_dev, v_dev, scale=scale) - - # ── CPU reference ────────────────────────────────────────────────────── - with torch.no_grad(): - out_cpu = model.cpu()(q, k, v, scale=scale) - - max_diff = (out_dev.cpu() - out_cpu).abs().max().item() - - with torch.no_grad():#CPU reference - out_library = F.scaled_dot_product_attention(q, k, v, attn_mask=None, dropout_p=0.0, is_causal=False, enable_gqa=True) - - max_diff_library = (out_library.cpu() - out_cpu).abs().max().item() - - print(f"[GQADecodeOptimized] seq_len={seq_len}, tile_size={tile_size}") - print(f" max |npu - cpu| = {max_diff:.6f}") - print(f" npu out max = {out_dev.cpu().abs().max().item():.6f}") - print(f" cpu out max = {out_cpu.abs().max().item():.6f}") - print(f" library out max = {out_library.abs().max().item():.6f}") - print(" PASS" if max_diff < 0.05 else " FAIL (diff too large)") - - - - -if __name__ == "__main__": - argparser = argparse.ArgumentParser(description="Test GQA Attention Implementations") - argparser.add_argument("--model", type=str, default="LLAMA4_TP8", choices=MODEL_CONFIGS.keys(), help="Model configuration to test") - argparser.add_argument("--context_length", type=int, default=10240, help="Sequence length (context length) for the attention test") - argparser.add_argument("--tile_size", type=int, default=4096, help="Tile size for the optimized attention implementation") - args = argparser.parse_args() - model = args.model - base_dir = os.environ.get("TORCHSIM_DIR", default="/workspace/PyTorchSim") - sys.path.append(base_dir) - test_gqa_decode_optimized(model=model, device=device, seq_len=args.context_length, tile_size=args.tile_size) diff --git a/tests/ops/attention/test_sdpa.py b/tests/ops/attention/test_sdpa.py index c4825731..4c52c3c0 100644 --- a/tests/ops/attention/test_sdpa.py +++ b/tests/ops/attention/test_sdpa.py @@ -10,25 +10,27 @@ device = torch.device("npu:0") # --------------------------------------------------------------------------- -# Default sweep configs - edit here to change what gets tested +# Unified SDPA test: Flash attention (MHA) and GQA, each in prefill and decode, +# swept over several dimensions and compared against the CPU MATH backend +# (numerical ground truth). +# +# - prefill : query length L == S (full sequence) +# - decode : query length L == 1 (single new token over a KV cache of length S) +# - MHA : query heads == kv heads (enable_gqa=False) +# - GQA : query heads == ratio * kv heads (enable_gqa=True) # --------------------------------------------------------------------------- -SDPA_DEFAULTS = dict( - n_batch_list = [1, 4, 8, 16], - n_head_list = [4, 6, 8, 12], - n_token_list = [128, 256, 512, 1024], - head_dim_list = [32, 64, 128], - is_causal = False, -) - -GQA_DEFAULTS = dict( - batch_list = [1], - num_kv_heads = 1, - gqa_ratios = [4, 5, 8, 16], # Hq = ratio * num_kv_heads - seq_len_list = [128, 256, 1024], - head_dim_list = [64, 128], - query_len = 1, # decode shape: Lq == 1 - is_causal = True, -) +BATCH_LIST = [1, 2, 4] # batch size +HEAD_LIST = [1, 4, 8] # MHA: query == kv heads +SEQ_LIST = [128, 256, 512] # KV sequence length S (multiples of tile_s) +HEAD_DIM_LIST = [64, 128] # head dim D (requires e == ev) + +# GQA head layout as (num_kv_heads, group_ratio); Hq = num_kv_heads * ratio. +GQA_HEAD_CONFIGS = [ + (1, 2), (1, 4), (1, 5), (1, 8), (1, 16), + (2, 8), (4, 3), (4, 8), (8, 8), +] +GQA_SEQ_LIST = [128, 256, 512] +GQA_HEAD_DIM_LIST = [64, 128] # --------------------------------------------------------------------------- @@ -43,17 +45,23 @@ def clear_caches(): FxGraphCache.clear() -def assert_close(name, out, cpu_out, rtol=1e-4, atol=1e-4): - msg = f"|{name} Test Passed|" +_RESULTS = {"pass": 0, "fail": 0, "failed_names": []} + + +def assert_close(name, out, cpu_out, rtol=1e-2, atol=1e-2): + """Compare device output vs CPU MATH reference. Records the result and keeps + going (does not abort) so the whole sweep runs; the summary/exit code is + emitted at the end of __main__.""" if torch.allclose(out.cpu(), cpu_out, rtol=rtol, atol=atol): - print("-" * len(msg)) - print(msg) - print("-" * len(msg)) - else: - print(f"[FAIL] {name}") - print(" device out:", out.cpu()) - print(" cpu out:", cpu_out) - exit(1) + print(f"[PASS] {name}") + _RESULTS["pass"] += 1 + return True + print(f"[FAIL] {name}") + print(" device out:", out.cpu()) + print(" cpu out:", cpu_out) + _RESULTS["fail"] += 1 + _RESULTS["failed_names"].append(name) + return False def _run_sdpa(device, q, k, v, **kwargs): @@ -63,83 +71,97 @@ def _run_sdpa(device, q, k, v, **kwargs): def _cpu_sdpa(q, k, v, **kwargs): - """Run reference SDPA on CPU.""" - return F.scaled_dot_product_attention(q.cpu(), k.cpu(), v.cpu(), **kwargs) + """Reference SDPA on CPU. Force the MATH backend (numerical ground truth): + it supports GQA and is independent of any outer sdpa_kernel() context, so it + works even when the device run is wrapped in a flash-only context.""" + with torch.nn.attention.sdpa_kernel([torch.nn.attention.SDPBackend.MATH]): + return F.scaled_dot_product_attention(q.cpu(), k.cpu(), v.cpu(), **kwargs) -# --------------------------------------------------------------------------- -# Tests -# --------------------------------------------------------------------------- -def test_sdpa( - device, - n_batch_list = SDPA_DEFAULTS["n_batch_list"], - n_head_list = SDPA_DEFAULTS["n_head_list"], - n_token_list = SDPA_DEFAULTS["n_token_list"], - head_dim_list = SDPA_DEFAULTS["head_dim_list"], - is_causal = SDPA_DEFAULTS["is_causal"], -): - torch.manual_seed(0) - sdpa_kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=is_causal) - - for B in n_batch_list: - for H in n_head_list: - for S in n_token_list: - for D in head_dim_list: - clear_caches() - q = torch.rand(B, H, S, D, dtype=torch.float32) - k = torch.rand(B, H, S, D, dtype=torch.float32) - v = torch.rand(B, H, S, D, dtype=torch.float32) - - out = _run_sdpa(device, q, k, v, **sdpa_kwargs) - cpu_out = _cpu_sdpa(q, k, v, **sdpa_kwargs) - - assert_close(f"SDPA(B:{B}, H:{H}, S:{S}, D:{D})", out, cpu_out) - - print("All SDPA tests passed!") - - -def test_gqa( - device, - batch_list = GQA_DEFAULTS["batch_list"], - num_kv_heads = GQA_DEFAULTS["num_kv_heads"], - gqa_ratios = GQA_DEFAULTS["gqa_ratios"], - seq_len_list = GQA_DEFAULTS["seq_len_list"], - head_dim_list= GQA_DEFAULTS["head_dim_list"], - query_len = GQA_DEFAULTS["query_len"], - is_causal = GQA_DEFAULTS["is_causal"], -): - """ - GQA sweep: q shape (B, Hq, Lq, D), kv shape (B, H, S, D). - Hq = ratio * num_kv_heads for each ratio in gqa_ratios. - """ - torch.manual_seed(0) - sdpa_kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=is_causal, enable_gqa=True) +def _check(name, q, k, v, **kwargs): + """Compile+run on device, compare against CPU MATH, record result.""" + clear_caches() + out = _run_sdpa(device, q, k, v, **kwargs) + cpu_out = _cpu_sdpa(q, k, v, **kwargs) + assert_close(name, out, cpu_out) - for B in batch_list: - for S in seq_len_list: - for D in head_dim_list: - for ratio in gqa_ratios: - Hq = ratio * num_kv_heads - clear_caches() - q = torch.rand(B, Hq, query_len, D, dtype=torch.float32) - k = torch.rand(B, num_kv_heads, S, D, dtype=torch.float32) - v = torch.rand(B, num_kv_heads, S, D, dtype=torch.float32) - out = _run_sdpa(device, q, k, v, **sdpa_kwargs) - cpu_out = _cpu_sdpa(q, k, v, **sdpa_kwargs) +# --------------------------------------------------------------------------- +# Flash attention (MHA: query heads == kv heads) +# --------------------------------------------------------------------------- +def test_flash_prefill(device): + """MHA prefill: q/k/v = (B, H, S, D), query length L == S.""" + kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=False) + for B in BATCH_LIST: + for H in HEAD_LIST: + for S in SEQ_LIST: + for D in HEAD_DIM_LIST: + q = torch.rand(B, H, S, D, dtype=torch.float16) + k = torch.rand(B, H, S, D, dtype=torch.float16) + v = torch.rand(B, H, S, D, dtype=torch.float16) + _check(f"flash-prefill(B{B},H{H},S{S},D{D})", q, k, v, **kwargs) + + +def test_flash_decode(device): + """MHA decode: q = (B, H, 1, D), k/v = (B, H, S, D), query length L == 1.""" + kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=False) + for B in BATCH_LIST: + for H in HEAD_LIST: + for S in SEQ_LIST: + for D in HEAD_DIM_LIST: + q = torch.rand(B, H, 1, D, dtype=torch.float16) + k = torch.rand(B, H, S, D, dtype=torch.float16) + v = torch.rand(B, H, S, D, dtype=torch.float16) + _check(f"flash-decode(B{B},H{H},S{S},D{D},L1)", q, k, v, **kwargs) - assert_close( - f"GQA(B:{B}, Hq:{Hq}, H:{num_kv_heads}, S:{S}, D:{D})", - out, cpu_out, - ) - print("All GQA tests passed!") +# --------------------------------------------------------------------------- +# GQA (query heads = ratio * kv heads) +# --------------------------------------------------------------------------- +def test_gqa_prefill(device): + """GQA prefill: q = (B, Hq, S, D), k/v = (B, Hkv, S, D), Hq = ratio*Hkv, L == S.""" + kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=False, enable_gqa=True) + for B in BATCH_LIST: + for Hkv, ratio in GQA_HEAD_CONFIGS: + Hq = ratio * Hkv + for S in GQA_SEQ_LIST: + for D in GQA_HEAD_DIM_LIST: + q = torch.rand(B, Hq, S, D, dtype=torch.float16) + k = torch.rand(B, Hkv, S, D, dtype=torch.float16) + v = torch.rand(B, Hkv, S, D, dtype=torch.float16) + _check(f"gqa-prefill(B{B},Hq{Hq},Hkv{Hkv},S{S},D{D})", q, k, v, **kwargs) + + +def test_gqa_decode(device): + """GQA decode: q = (B, Hq, 1, D), k/v = (B, Hkv, S, D), Hq = ratio*Hkv, L == 1.""" + kwargs = dict(attn_mask=None, dropout_p=0.0, is_causal=False, enable_gqa=True) + for B in BATCH_LIST: + for Hkv, ratio in GQA_HEAD_CONFIGS: + Hq = ratio * Hkv + for S in GQA_SEQ_LIST: + for D in GQA_HEAD_DIM_LIST: + q = torch.rand(B, Hq, 1, D, dtype=torch.float16) + k = torch.rand(B, Hkv, S, D, dtype=torch.float16) + v = torch.rand(B, Hkv, S, D, dtype=torch.float16) + _check(f"gqa-decode(B{B},Hq{Hq},Hkv{Hkv},S{S},D{D},L1)", q, k, v, **kwargs) if __name__ == "__main__": + torch.manual_seed(0) with torch.nn.attention.sdpa_kernel([torch.nn.attention.SDPBackend.FLASH_ATTENTION]): - test_sdpa(device) - #test_gqa(device) - - # Example: quick single-config run - # test_gqa(device, batch_list=[1], gqa_ratios=[5], seq_len_list=[32], head_dim_list=[128]) + # test_flash_prefill(device) + # test_flash_decode(device) + # test_gqa_prefill(device) + test_gqa_decode(device) + + total = _RESULTS["pass"] + _RESULTS["fail"] + print("=" * 60) + print(f"SDPA tests: {_RESULTS['pass']}/{total} passed, {_RESULTS['fail']} failed") + if _RESULTS["failed_names"]: + print("Failed:") + for n in _RESULTS["failed_names"]: + print(f" - {n}") + print("=" * 60) + sys.exit(1 if _RESULTS["fail"] else 0) + + \ No newline at end of file