|
146 | 146 | _SMFP8_BWD_CLEAR_TOTAL = _SMFP8_BWD_B * _SMFP8_BWD_SKV * _SMFP8_BWD_G * _SMFP8_BWD_K |
147 | 147 | _SMFP8_BWD_CLEAR_THREADS = 256 |
148 | 148 | _SMFP8_PER_TOKEN_QUANT_THREADS = 256 |
| 149 | +_SMFP8_PREPARE_Q_ROWS = 1 |
| 150 | +_SMFP8_PREPARE_KV_ROWS = 1 |
| 151 | +_SMFP8_PREPARE_ROWS = _SMFP8_PREPARE_Q_ROWS + _SMFP8_PREPARE_KV_ROWS |
| 152 | +_SMFP8_PREPARE_K = 64 |
| 153 | +_SMFP8_PREPARE_INPUT_DTYPE = "float32" |
| 154 | +_SMFP8_PREPARE_STORAGE_DTYPE = "uint8" |
149 | 155 |
|
150 | 156 |
|
151 | 157 | @dataclass(frozen=True) |
@@ -2709,6 +2715,100 @@ def fp8_per_token_quant( |
2709 | 2715 | return fp8_per_token_quant |
2710 | 2716 |
|
2711 | 2717 |
|
| 2718 | +def make_fp8_sparse_mla_prepare_kernel( |
| 2719 | + *, |
| 2720 | + q_rows: int, |
| 2721 | + kv_rows: int, |
| 2722 | + K: int, |
| 2723 | + in_dtype: str, |
| 2724 | + storage_dtype: str = "uint8", |
| 2725 | +) -> Any: |
| 2726 | + """Build the FP8 prepare producer PrimFunc for fused train-block schedules. |
| 2727 | +
|
| 2728 | + ``post_y`` is a flat q-then-kv carrier. The kernel emits first-class |
| 2729 | + prepared Sparse-MLA FP8 buffers and per-token scales so the downstream |
| 2730 | + apply node can stay inside the same TileLang/TVM region. |
| 2731 | + """ |
| 2732 | + |
| 2733 | + if q_rows <= 0 or kv_rows <= 0 or K <= 0: |
| 2734 | + raise ValueError("q_rows, kv_rows, and K must be positive") |
| 2735 | + if storage_dtype not in {"uint8", "float8_e4m3"}: |
| 2736 | + raise ValueError( |
| 2737 | + "storage_dtype must be 'uint8' or 'float8_e4m3'; " |
| 2738 | + f"got {storage_dtype!r}" |
| 2739 | + ) |
| 2740 | + |
| 2741 | + import tilelang.language as T |
| 2742 | + from tilelang.tileop.metal_quant import float_to_fp8_e4m3fn_bits |
| 2743 | + |
| 2744 | + T = cast(Any, T) |
| 2745 | + g = globals() |
| 2746 | + g.update( |
| 2747 | + _SMFP8_PREPARE_Q_ROWS=int(q_rows), |
| 2748 | + _SMFP8_PREPARE_KV_ROWS=int(kv_rows), |
| 2749 | + _SMFP8_PREPARE_ROWS=int(q_rows) + int(kv_rows), |
| 2750 | + _SMFP8_PREPARE_K=int(K), |
| 2751 | + _SMFP8_PREPARE_INPUT_DTYPE=str(in_dtype), |
| 2752 | + _SMFP8_PREPARE_STORAGE_DTYPE=str(storage_dtype), |
| 2753 | + ) |
| 2754 | + |
| 2755 | + def encode_fp8(normalized): |
| 2756 | + if storage_dtype == "uint8": |
| 2757 | + return float_to_fp8_e4m3fn_bits(normalized) |
| 2758 | + return T.cast(normalized, "float8_e4m3") |
| 2759 | + |
| 2760 | + @T.prim_func |
| 2761 | + def fp8_sparse_mla_prepare( |
| 2762 | + post_y: T.Tensor( |
| 2763 | + (_SMFP8_PREPARE_ROWS * _SMFP8_PREPARE_K,), |
| 2764 | + _SMFP8_PREPARE_INPUT_DTYPE, |
| 2765 | + ), |
| 2766 | + q_fp8: T.Tensor( |
| 2767 | + (_SMFP8_PREPARE_Q_ROWS * _SMFP8_PREPARE_K,), |
| 2768 | + _SMFP8_PREPARE_STORAGE_DTYPE, |
| 2769 | + ), |
| 2770 | + q_scale: T.Tensor((_SMFP8_PREPARE_Q_ROWS,), "float32"), |
| 2771 | + kv_fp8: T.Tensor( |
| 2772 | + (_SMFP8_PREPARE_KV_ROWS * _SMFP8_PREPARE_K,), |
| 2773 | + _SMFP8_PREPARE_STORAGE_DTYPE, |
| 2774 | + ), |
| 2775 | + kv_scale: T.Tensor((_SMFP8_PREPARE_KV_ROWS,), "float32"), |
| 2776 | + ): |
| 2777 | + with T.Kernel(_SMFP8_PREPARE_ROWS, threads=_SMFP8_PER_TOKEN_QUANT_THREADS) as row: |
| 2778 | + x_abs = T.alloc_fragment((_SMFP8_PREPARE_K,), "float32") |
| 2779 | + row_amax = T.alloc_fragment((1,), "float32") |
| 2780 | + base = row * _SMFP8_PREPARE_K |
| 2781 | + for k in T.Parallel(_SMFP8_PREPARE_K): |
| 2782 | + x_abs[k] = T.abs(T.cast(post_y[base + k], "float32")) |
| 2783 | + T.reduce_max(x_abs, row_amax, dim=0, clear=True) |
| 2784 | + row_scale = T.max( |
| 2785 | + row_amax[0] * T.cast(1.0 / 448.0, "float32"), |
| 2786 | + T.cast(1.0e-12, "float32"), |
| 2787 | + ) |
| 2788 | + if row < _SMFP8_PREPARE_Q_ROWS: |
| 2789 | + if T.get_thread_binding(0) == 0: |
| 2790 | + q_scale[row] = row_scale |
| 2791 | + for k in T.Parallel(_SMFP8_PREPARE_K): |
| 2792 | + normalized = T.alloc_var("float32") |
| 2793 | + normalized = T.cast(post_y[base + k], "float32") / row_scale |
| 2794 | + normalized = T.max(normalized, T.cast(-448.0, "float32")) |
| 2795 | + normalized = T.min(normalized, T.cast(448.0, "float32")) |
| 2796 | + q_fp8[base + k] = encode_fp8(normalized) |
| 2797 | + else: |
| 2798 | + kv_row = row - _SMFP8_PREPARE_Q_ROWS |
| 2799 | + kv_base = kv_row * _SMFP8_PREPARE_K |
| 2800 | + if T.get_thread_binding(0) == 0: |
| 2801 | + kv_scale[kv_row] = row_scale |
| 2802 | + for k in T.Parallel(_SMFP8_PREPARE_K): |
| 2803 | + normalized = T.alloc_var("float32") |
| 2804 | + normalized = T.cast(post_y[base + k], "float32") / row_scale |
| 2805 | + normalized = T.max(normalized, T.cast(-448.0, "float32")) |
| 2806 | + normalized = T.min(normalized, T.cast(448.0, "float32")) |
| 2807 | + kv_fp8[kv_base + k] = encode_fp8(normalized) |
| 2808 | + |
| 2809 | + return fp8_sparse_mla_prepare |
| 2810 | + |
| 2811 | + |
2712 | 2812 | @lru_cache(maxsize=128) |
2713 | 2813 | def _fp8_per_token_quant_tvm_ffi_kernel_for( |
2714 | 2814 | rows: int, |
@@ -3619,6 +3719,7 @@ def fp8_sparse_mla_indexed_qk_reduce_path_c_status( |
3619 | 3719 | "lower_fp8_sparse_mla_indexed_qk_reduce_msl", |
3620 | 3720 | "lower_fp8_sparse_mla_qk_reduce_msl", |
3621 | 3721 | "lower_fp8_sparse_mla_qk_msl", |
| 3722 | + "make_fp8_sparse_mla_prepare_kernel", |
3622 | 3723 | "make_fp8_sparse_mla_indexed_qk_reduce_kernel", |
3623 | 3724 | "make_fp8_sparse_mla_qk_reduce_kernel", |
3624 | 3725 | "make_fp8_sparse_mla_qk_kernel", |
|
0 commit comments