File size: 6,610 Bytes
88b8ef2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 | // ============================================================================
// Fused FP4 quantize + CUTLASS SFA/SFB tile-interleaved scale write.
//
// Implementation = kernel_quantize_fp4 (quantize_fp4_dynamic.cu) with the
// scale-store address replaced by the CUTLASS layout functor. Packed fp4
// elements layout is UNCHANGED (still linear [N, D/2]), only the scale
// byte goes to a different location.
// ============================================================================
#include "quantize_fp4_sfa.cuh"
#include <cuda_bf16.h>
#include <cuda_fp16.h>
#include <cuda_fp8.h>
#ifndef CUTLASS_ARCH_MMA_SM100_SUPPORTED
# define CUTLASS_ARCH_MMA_SM100_SUPPORTED 1
#endif
#if defined(CUTLASS_ARCH_MMA_SM100_SUPPORTED) || defined(__CUDA_ARCH__)
# include "cutlass/cutlass.h"
# include "cutlass/detail/sm100_blockscaled_layout.hpp"
# include "cute/tensor.hpp"
# define FV_HAVE_CUTLASS 1
#else
# define FV_HAVE_CUTLASS 0
#endif
namespace flash_rt {
namespace fp4 {
#if FV_HAVE_CUTLASS
using Cfg = cutlass::detail::Sm1xxBlockScaledConfig<16>;
// ── Device helpers (duplicated locally to stay additive — not linking against
// quantize_fp4_dynamic.cu so we don't risk ODR issues). Identical logic. ──
__device__ __forceinline__ uint8_t fp32_to_e2m1_sfa(float x) {
uint8_t sign = (x < 0.f) ? 0x8u : 0x0u;
float ax = fabsf(x);
uint8_t mant;
if (ax <= 0.25f) mant = 0u;
else if (ax <= 0.75f) mant = 1u;
else if (ax <= 1.25f) mant = 2u;
else if (ax <= 1.75f) mant = 3u;
else if (ax <= 2.5f) mant = 4u;
else if (ax <= 3.5f) mant = 5u;
else if (ax <= 5.0f) mant = 6u;
else mant = 7u;
return sign | mant;
}
__device__ __forceinline__ __nv_fp8_e4m3 quantize_ue4m3_sfa(float x) {
float v = fmaxf(x, 0.f);
return __nv_fp8_e4m3(v);
}
__device__ __forceinline__ float dequantize_ue4m3_sfa(__nv_fp8_e4m3 s) {
return static_cast<float>(s);
}
__device__ __forceinline__ float input_to_float(__half value) {
return __half2float(value);
}
__device__ __forceinline__ float input_to_float(__nv_bfloat16 value) {
return __bfloat162float(value);
}
// ── Fused kernel ──
// One thread per (row, 16-element block). Scale byte goes to
// dst_sfa[layout(row, block_idx*16, 0)].
template <typename Input, class LayoutSF>
__global__ void kernel_quantize_fp4_sfa(
const Input* __restrict__ src,
uint8_t* __restrict__ dst_packed,
uint8_t* __restrict__ dst_sfa, // raw byte view of the CUTLASS SFA/SFB buffer
LayoutSF layout,
int N, int D) {
const int block_idx = blockIdx.x * blockDim.x + threadIdx.x;
const int row = blockIdx.y;
const int n_blocks = D / 16;
if (row >= N || block_idx >= n_blocks) return;
const int base = row * D + block_idx * 16;
float vals[16];
float amax = 0.f;
#pragma unroll
for (int i = 0; i < 16; ++i) {
vals[i] = input_to_float(src[base + i]);
float a = fabsf(vals[i]);
if (a > amax) amax = a;
}
float desired = amax / 6.f;
if (desired < 1e-12f) desired = 1e-12f;
__nv_fp8_e4m3 bs_q = quantize_ue4m3_sfa(desired);
float bs_dq = dequantize_ue4m3_sfa(bs_q);
// ── CORE FUSION: direct SFA tile-layout write ──
// LayoutSF maps (row, k, L=0) → byte offset. k is the full-K coordinate;
// SFVecSize=16 is baked in so any k in [block*16, block*16+15] hits the
// same offset. Use block_idx*16 (same convention as reshape_scales_sfa.cu).
int sfa_off = layout(row, block_idx * 16, 0);
dst_sfa[sfa_off] = *reinterpret_cast<uint8_t*>(&bs_q);
// Packed fp4 elements: layout unchanged.
const int out_base = row * (D / 2) + block_idx * 8;
const float inv_bs = 1.f / bs_dq;
#pragma unroll
for (int p = 0; p < 8; ++p) {
float v_lo = vals[2 * p ] * inv_bs;
float v_hi = vals[2 * p + 1] * inv_bs;
uint8_t lo = fp32_to_e2m1_sfa(v_lo);
uint8_t hi = fp32_to_e2m1_sfa(v_hi);
dst_packed[out_base + p] = lo | (hi << 4);
}
}
#endif // FV_HAVE_CUTLASS
int quantize_fp4_dynamic_sfa_fp16(
const void* src_fp16, void* dst_packed, void* dst_sfa,
int N, int D, bool is_sfb, cudaStream_t stream) {
#if FV_HAVE_CUTLASS
if (D % 16 != 0) return -1;
const int n_blocks = D / 16;
const int threads = 128;
dim3 grid((n_blocks + threads - 1) / threads, N);
dim3 block(threads);
// Shape: SFA uses (M=N, 1, K=D, L=1); SFB uses (1, N=N, K=D, L=1).
auto shape = cute::make_shape(
is_sfb ? 1 : N,
is_sfb ? N : 1,
D, 1);
if (is_sfb) {
auto layout = Cfg::tile_atom_to_shape_SFB(shape);
kernel_quantize_fp4_sfa<__half><<<grid, block, 0, stream>>>(
reinterpret_cast<const __half*>(src_fp16),
reinterpret_cast<uint8_t*>(dst_packed),
reinterpret_cast<uint8_t*>(dst_sfa),
layout, N, D);
} else {
auto layout = Cfg::tile_atom_to_shape_SFA(shape);
kernel_quantize_fp4_sfa<__half><<<grid, block, 0, stream>>>(
reinterpret_cast<const __half*>(src_fp16),
reinterpret_cast<uint8_t*>(dst_packed),
reinterpret_cast<uint8_t*>(dst_sfa),
layout, N, D);
}
cudaError_t e = cudaGetLastError();
return (e == cudaSuccess) ? 0 : -static_cast<int>(e);
#else
(void)src_fp16; (void)dst_packed; (void)dst_sfa;
(void)N; (void)D; (void)is_sfb; (void)stream;
return -2;
#endif
}
int quantize_fp4_dynamic_sfa_bf16(
const void* src_bf16, void* dst_packed, void* dst_sfa,
int N, int D, bool is_sfb, cudaStream_t stream) {
#if FV_HAVE_CUTLASS
if (D % 16 != 0) return -1;
const int n_blocks = D / 16;
const int threads = 128;
dim3 grid((n_blocks + threads - 1) / threads, N);
dim3 block(threads);
auto shape = cute::make_shape(is_sfb ? 1 : N, is_sfb ? N : 1, D, 1);
if (is_sfb) {
auto layout = Cfg::tile_atom_to_shape_SFB(shape);
kernel_quantize_fp4_sfa<__nv_bfloat16><<<grid, block, 0, stream>>>(
reinterpret_cast<const __nv_bfloat16*>(src_bf16),
reinterpret_cast<uint8_t*>(dst_packed),
reinterpret_cast<uint8_t*>(dst_sfa), layout, N, D);
} else {
auto layout = Cfg::tile_atom_to_shape_SFA(shape);
kernel_quantize_fp4_sfa<__nv_bfloat16><<<grid, block, 0, stream>>>(
reinterpret_cast<const __nv_bfloat16*>(src_bf16),
reinterpret_cast<uint8_t*>(dst_packed),
reinterpret_cast<uint8_t*>(dst_sfa), layout, N, D);
}
cudaError_t e = cudaGetLastError();
return (e == cudaSuccess) ? 0 : -static_cast<int>(e);
#else
(void)src_bf16; (void)dst_packed; (void)dst_sfa;
(void)N; (void)D; (void)is_sfb; (void)stream;
return -2;
#endif
}
} // namespace fp4
} // namespace flash_rt
|