Skip to content
KernelIndex
Search⌘K

submission 69480

snowclipsed · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

No package. Vendor the mirrored source: 245 lines, June 9 Researcher Reciprocity License v1.0.

nvfp4.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-nvfp4-gemv-69480?include=source"
interfacepython
Compatibility
measured onNVIDIA B200
declared hardwareNVIDIA B200
architecturessm_100
dtypesfp8_e4m3, nvfp4

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
NVFP4 GEMVsuite of 3 cases
NVIDIA B200
989.1µs
#613 of 678
2025-11-11

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:8905377f0a1d36d20e5d83a899147cafa5b25758bb30fd5746cb35bef1f50b3e
license declaredunknown
license concludedunknown
authorssnowclipsed
imported2026-08-15

Techniques

Extracted from the mirrored source by pattern, never inferred. Each row cites its line.

fp4NVFP4 Batched GEMV Optimization - Research-Based Approach

Kernel source

nvfp4.py245 lines
import torch
import triton
import triton.language as tl
from task import input_t, output_t

"""
NVFP4 Batched GEMV Optimization - Research-Based Approach

KEY INSIGHTS:
1. NVFP4 has dual scaling: FP8 per 16 elements + global FP32
2. Dequantization happens IN HARDWARE on Blackwell Tensor Cores
3. torch._scaled_mm is optimized for GEMM not GEMV
4. Sequential batch loop kills parallelism
5. Scale blocking transformation can be overlapped

OPTIMIZATION TARGETS:
- Eliminate sequential batch processing
- Use async streams for computation-communication overlap
- Optimize scale factor transformation with better memory access
- Leverage torch's native operations where possible
"""

def ceil_div(a, b):
    return (a + b - 1) // b

# ============================================================================
# APPROACH 1: Stream-based Overlap (Quick Win)
# Hide scale transformation behind previous batch's computation
# ============================================================================

@triton.jit
def blocked_transform_optimized(
    inp, out, M, K, L,
    s_im, s_ik, s_il, s_ol, s_oe,
    BLK: tl.constexpr
):
    """Optimized scale factor blocking with coalesced access"""
    pid_l = tl.program_id(0)
    pid_b = tl.program_id(1)
    
    mk = M * K
    offs = pid_b * BLK + tl.arange(0, BLK)
    mask = offs < mk
    
    # Decompose to (i, j)
    i = offs // K
    j = offs % K
    
    # Blocking parameters
    nrb = (M + 127) // 128
    ncb = (K + 3) // 4
    
    # Block indices
    rb, ri = i // 128, i % 128
    cb, ci = j // 4, j % 4
    
    # Permute: view(nrb,128,ncb,4) -> permute(0,2,1,3)
    perm = rb * ncb * 512 + cb * 512 + ri * 4 + ci
    
    # Reshape and transpose
    chunk = perm // 512
    rest = perm % 512
    d1, d2, d3 = rest // 128, (rest % 128) // 4, rest % 4
    
    out_idx = chunk * 512 + d2 * 16 + d1 * 4 + d3
    
    # Coalesced load/store
    inp_idx = pid_l * s_il + i * s_im + j * s_ik
    out_idx_final = pid_l * s_ol + out_idx * s_oe
    
    val = tl.load(inp + inp_idx, mask=mask)
    tl.store(out + out_idx_final, val, mask=mask)

def transform_scales_async(tensor, stream):
    """Transform scales asynchronously on given stream"""
    M, K, L = tensor.shape
    mk = M * K
    result = torch.empty((L, mk), dtype=tensor.dtype, device='cuda')
    
    t_gpu = tensor if tensor.is_cuda else tensor.cuda()
    
    with torch.cuda.stream(stream):
        BLK = 256
        grid = (L, (mk + BLK - 1) // BLK)
        blocked_transform_optimized[grid](
            t_gpu, result, M, K, L,
            t_gpu.stride(0), t_gpu.stride(1), t_gpu.stride(2),
            result.stride(0), result.stride(1),
            BLK=BLK
        )
    
    return [result[i] for i in range(L)]

def custom_kernel_streams(data: input_t) -> output_t:
    """
    Stream-based overlap: Transform scales for batch N+1 
    while computing batch N
    """
    a, b, sfa_cpu, sfb_cpu, _, _, c = data
    m, _, l = c.shape
    
    # Create streams for overlap
    compute_stream = torch.cuda.Stream()
    transform_stream = torch.cuda.Stream()
    
    # Transform first batch
    with torch.cuda.stream(transform_stream):
        sfa_list = transform_scales_async(sfa_cpu, transform_stream)
        sfb_list = transform_scales_async(sfb_cpu, transform_stream)
    
    # Wait for first transformation
    transform_stream.synchronize()
    
    # Process all batches
    for i in range(l):
        with torch.cuda.stream(compute_stream):
            res = torch._scaled_mm(
                a[:, :, i],
                b[:, :, i].transpose(0, 1),
                sfa_list[i],
                sfb_list[i],
                bias=None,
                out_dtype=torch.float16,
            )
            c[:, 0, i] = res[:, 0]
    
    compute_stream.synchronize()
    return c

# ============================================================================
# APPROACH 2: Pre-transform + Optimized Loop (Better)
# Transform all scales once, then tight loop
# ============================================================================

def custom_kernel_pretransform(data: input_t) -> output_t:
    """
    Pre-transform all scales, then run tight loop.
    Reduces per-batch overhead.
    """
    a, b, sfa_cpu, sfb_cpu, _, _, c = data
    m, _, l = c.shape
    
    # Transform ALL scales at once (single kernel launch overhead)
    M_sfa, K_sfa, _ = sfa_cpu.shape
    M_sfb, K_sfb, _ = sfb_cpu.shape
    
    sfa_all = torch.empty((l, M_sfa * K_sfa), dtype=sfa_cpu.dtype, device='cuda')
    sfb_all = torch.empty((l, M_sfb * K_sfb), dtype=sfb_cpu.dtype, device='cuda')
    
    # Single transformation for all batches
    sfa_gpu = sfa_cpu if sfa_cpu.is_cuda else sfa_cpu.cuda()
    sfb_gpu = sfb_cpu if sfb_cpu.is_cuda else sfb_cpu.cuda()
    
    BLK = 256
    grid_sfa = (l, ceil_div(M_sfa * K_sfa, BLK))
    grid_sfb = (l, ceil_div(M_sfb * K_sfb, BLK))
    
    blocked_transform_optimized[grid_sfa](
        sfa_gpu, sfa_all, M_sfa, K_sfa, l,
        sfa_gpu.stride(0), sfa_gpu.stride(1), sfa_gpu.stride(2),
        sfa_all.stride(0), sfa_all.stride(1),
        BLK=BLK
    )
    
    blocked_transform_optimized[grid_sfb](
        sfb_gpu, sfb_all, M_sfb, K_sfb, l,
        sfb_gpu.stride(0), sfb_gpu.stride(1), sfb_gpu.stride(2),
        sfb_all.stride(0), sfb_all.stride(1),
        BLK=BLK
    )
    
    # Tight loop over pre-transformed scales
    for i in range(l):
        res = torch._scaled_mm(
            a[:, :, i],
            b[:, :, i].transpose(0, 1),
            sfa_all[i],
            sfb_all[i],
            bias=None,
            out_dtype=torch.float16,
        )
        c[:, 0, i] = res[:, 0]
    
    return c

# ============================================================================
# APPROACH 3: Fused Transformation (Best for large L)
# Combine all transformations into single grid
# ============================================================================

def custom_kernel_fused(data: input_t) -> output_t:
    """
    Fuse both scale transformations into one kernel grid.
    Best when L is large.
    """
    a, b, sfa_cpu, sfb_cpu, _, _, c = data
    m, _, l = c.shape
    
    M_sfa, K_sfa, _ = sfa_cpu.shape
    M_sfb, K_sfb, _ = sfb_cpu.shape
    
    # Allocate output
    sfa_blocked = torch.empty((l, M_sfa * K_sfa), dtype=sfa_cpu.dtype, device='cuda')
    sfb_blocked = torch.empty((l, M_sfb * K_sfb), dtype=sfb_cpu.dtype, device='cuda')
    
    # Move to GPU
    sfa_gpu = sfa_cpu.cuda() if not sfa_cpu.is_cuda else sfa_cpu
    sfb_gpu = sfb_cpu.cuda() if not sfb_cpu.is_cuda else sfb_cpu
    
    # Launch both transformations (they'll run concurrently)
    BLK = 256
    blocked_transform_optimized[(l, ceil_div(M_sfa * K_sfa, BLK))](
        sfa_gpu, sfa_blocked, M_sfa, K_sfa, l,
        sfa_gpu.stride(0), sfa_gpu.stride(1), sfa_gpu.stride(2),
        sfa_blocked.stride(0), sfa_blocked.stride(1),
        BLK=BLK
    )
    blocked_transform_optimized[(l, ceil_div(M_sfb * K_sfb, BLK))](
        sfb_gpu, sfb_blocked, M_sfb, K_sfb, l,
        sfb_gpu.stride(0), sfb_gpu.stride(1), sfb_gpu.stride(2),
        sfb_blocked.stride(0), sfb_blocked.stride(1),
        BLK=BLK
    )
    
    # Main compute loop (still sequential, but tight)
    for i in range(l):
        res = torch._scaled_mm(
            a[:, :, i],
            b[:, :, i].transpose(0, 1),
            sfa_blocked[i],
            sfb_blocked[i],
            bias=None,
            out_dtype=torch.float16,
        )
        c[:, 0, i] = res[:, 0]
    
    return c

# Entry point - use best approach
def custom_kernel(data: input_t) -> output_t:
    """
    Main entry - chooses best approach based on problem size.
    For now, use fused transformation (Approach 3).
    """
    return custom_kernel_fused(data)
scrolls · 245 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Changes from previous submission

Against this author's previous submission submission 69271.

import torch
+ import triton
+ import triton.language as tl
from task import input_t, output_t
- sf_vec_size = 16
+ """
+ NVFP4 Batched GEMV Optimization - Research-Based Approach
+ KEY INSIGHTS:
+ 1. NVFP4 has dual scaling: FP8 per 16 elements + global FP32
+ 2. Dequantization happens IN HARDWARE on Blackwell Tensor Cores
+ 3. torch._scaled_mm is optimized for GEMM not GEMV
+ 4. Sequential batch loop kills parallelism
+ 5. Scale blocking transformation can be overlapped
+
+ OPTIMIZATION TARGETS:
+ - Eliminate sequential batch processing
+ - Use async streams for computation-communication overlap
+ - Optimize scale factor transformation with better memory access
+ - Leverage torch's native operations where possible
+ """
+
def ceil_div(a, b):
return (a + b - 1) // b
- def to_blocked(input_matrix):
- rows, cols = input_matrix.shape
- n_row_blocks = ceil_div(rows, 128)
- n_col_blocks = ceil_div(cols, 4)
- padded = input_matrix
- blocks = padded.view(n_row_blocks, 128, n_col_blocks, 4).permute(0, 2, 1, 3)
- rearranged = blocks.reshape(-1, 4, 32, 4).transpose(1, 2).reshape(-1, 32, 16)
- return rearranged.flatten()
+ # ============================================================================
+ # APPROACH 1: Stream-based Overlap (Quick Win)
+ # Hide scale transformation behind previous batch's computation
+ # ============================================================================
- def custom_kernel(data: input_t) -> output_t:
- a_ref, b_ref, sfa_ref_cpu, sfb_ref_cpu, _, _, c_ref = data
+ @triton.jit
+ def blocked_transform_optimized(
+ inp, out, M, K, L,
+ s_im, s_ik, s_il, s_ol, s_oe,
+ BLK: tl.constexpr
+ ):
+ """Optimized scale factor blocking with coalesced access"""
+ pid_l = tl.program_id(0)
+ pid_b = tl.program_id(1)
- m, _, l = c_ref.shape
+ mk = M * K
+ offs = pid_b * BLK + tl.arange(0, BLK)
+ mask = offs < mk
- # Batch convert all scale factors upfront
- sfa_list = [to_blocked(sfa_ref_cpu[:, :, i]).cuda() for i in range(l)]
- sfb_list = [to_blocked(sfb_ref_cpu[:, :, i]).cuda() for i in range(l)]
+ # Decompose to (i, j)
+ i = offs // K
+ j = offs % K
- # Process all batches with pre-converted scales
- for l_idx in range(l):
+ # Blocking parameters
+ nrb = (M + 127) // 128
+ ncb = (K + 3) // 4
+
+ # Block indices
+ rb, ri = i // 128, i % 128
+ cb, ci = j // 4, j % 4
+
+ # Permute: view(nrb,128,ncb,4) -> permute(0,2,1,3)
+ perm = rb * ncb * 512 + cb * 512 + ri * 4 + ci
+
+ # Reshape and transpose
+ chunk = perm // 512
+ rest = perm % 512
+ d1, d2, d3 = rest // 128, (rest % 128) // 4, rest % 4
+
+ out_idx = chunk * 512 + d2 * 16 + d1 * 4 + d3
+
+ # Coalesced load/store
+ inp_idx = pid_l * s_il + i * s_im + j * s_ik
+ out_idx_final = pid_l * s_ol + out_idx * s_oe
+
+ val = tl.load(inp + inp_idx, mask=mask)
+ tl.store(out + out_idx_final, val, mask=mask)
+
+ def transform_scales_async(tensor, stream):
+ """Transform scales asynchronously on given stream"""
+ M, K, L = tensor.shape
+ mk = M * K
+ result = torch.empty((L, mk), dtype=tensor.dtype, device='cuda')
+
+ t_gpu = tensor if tensor.is_cuda else tensor.cuda()
+
+ with torch.cuda.stream(stream):
+ BLK = 256
+ grid = (L, (mk + BLK - 1) // BLK)
+ blocked_transform_optimized[grid](
+ t_gpu, result, M, K, L,
+ t_gpu.stride(0), t_gpu.stride(1), t_gpu.stride(2),
+ result.stride(0), result.stride(1),
+ BLK=BLK
+ )
+
+ return [result[i] for i in range(L)]
+
+ def custom_kernel_streams(data: input_t) -> output_t:
+ """
+ Stream-based overlap: Transform scales for batch N+1
+ while computing batch N
+ """
+ a, b, sfa_cpu, sfb_cpu, _, _, c = data
+ m, _, l = c.shape
+
+ # Create streams for overlap
+ compute_stream = torch.cuda.Stream()
+ transform_stream = torch.cuda.Stream()
+
+ # Transform first batch
+ with torch.cuda.stream(transform_stream):
+ sfa_list = transform_scales_async(sfa_cpu, transform_stream)
+ sfb_list = transform_scales_async(sfb_cpu, transform_stream)
+
+ # Wait for first transformation
+ transform_stream.synchronize()
+
+ # Process all batches
+ for i in range(l):
+ with torch.cuda.stream(compute_stream):
+ res = torch._scaled_mm(
+ a[:, :, i],
+ b[:, :, i].transpose(0, 1),
+ sfa_list[i],
+ sfb_list[i],
+ bias=None,
+ out_dtype=torch.float16,
+ )
+ c[:, 0, i] = res[:, 0]
+
+ compute_stream.synchronize()
+ return c
+
+ # ============================================================================
+ # APPROACH 2: Pre-transform + Optimized Loop (Better)
+ # Transform all scales once, then tight loop
+ # ============================================================================
+
+ def custom_kernel_pretransform(data: input_t) -> output_t:
+ """
+ Pre-transform all scales, then run tight loop.
+ Reduces per-batch overhead.
+ """
+ a, b, sfa_cpu, sfb_cpu, _, _, c = data
+ m, _, l = c.shape
+
+ # Transform ALL scales at once (single kernel launch overhead)
+ M_sfa, K_sfa, _ = sfa_cpu.shape
+ M_sfb, K_sfb, _ = sfb_cpu.shape
+
+ sfa_all = torch.empty((l, M_sfa * K_sfa), dtype=sfa_cpu.dtype, device='cuda')
+ sfb_all = torch.empty((l, M_sfb * K_sfb), dtype=sfb_cpu.dtype, device='cuda')
+
+ # Single transformation for all batches
+ sfa_gpu = sfa_cpu if sfa_cpu.is_cuda else sfa_cpu.cuda()
+ sfb_gpu = sfb_cpu if sfb_cpu.is_cuda else sfb_cpu.cuda()
+
+ BLK = 256
+ grid_sfa = (l, ceil_div(M_sfa * K_sfa, BLK))
+ grid_sfb = (l, ceil_div(M_sfb * K_sfb, BLK))
+
+ blocked_transform_optimized[grid_sfa](
+ sfa_gpu, sfa_all, M_sfa, K_sfa, l,
+ sfa_gpu.stride(0), sfa_gpu.stride(1), sfa_gpu.stride(2),
+ sfa_all.stride(0), sfa_all.stride(1),
+ BLK=BLK
+ )
+
+ blocked_transform_optimized[grid_sfb](
+ sfb_gpu, sfb_all, M_sfb, K_sfb, l,
+ sfb_gpu.stride(0), sfb_gpu.stride(1), sfb_gpu.stride(2),
+ sfb_all.stride(0), sfb_all.stride(1),
+ BLK=BLK
+ )
+
+ # Tight loop over pre-transformed scales
+ for i in range(l):
res = torch._scaled_mm(
- a_ref[:, :, l_idx],
- b_ref[:, :, l_idx].transpose(0, 1),
- sfa_list[l_idx],
- sfb_list[l_idx],
+ a[:, :, i],
+ b[:, :, i].transpose(0, 1),
+ sfa_all[i],
+ sfb_all[i],
bias=None,
out_dtype=torch.float16,
)
- c_ref[:, 0, l_idx] = res[:, 0]
+ c[:, 0, i] = res[:, 0]
- return c_ref
No newline at end of file
+ return c
+
+ # ============================================================================
+ # APPROACH 3: Fused Transformation (Best for large L)
+ # Combine all transformations into single grid
+ # ============================================================================
+
+ def custom_kernel_fused(data: input_t) -> output_t:
+ """
+ Fuse both scale transformations into one kernel grid.
+ Best when L is large.
+ """
+ a, b, sfa_cpu, sfb_cpu, _, _, c = data
+ m, _, l = c.shape
+
+ M_sfa, K_sfa, _ = sfa_cpu.shape
+ M_sfb, K_sfb, _ = sfb_cpu.shape
+
+ # Allocate output
+ sfa_blocked = torch.empty((l, M_sfa * K_sfa), dtype=sfa_cpu.dtype, device='cuda')
+ sfb_blocked = torch.empty((l, M_sfb * K_sfb), dtype=sfb_cpu.dtype, device='cuda')
+
+ # Move to GPU
+ sfa_gpu = sfa_cpu.cuda() if not sfa_cpu.is_cuda else sfa_cpu
+ sfb_gpu = sfb_cpu.cuda() if not sfb_cpu.is_cuda else sfb_cpu
+
+ # Launch both transformations (they'll run concurrently)
+ BLK = 256
+ blocked_transform_optimized[(l, ceil_div(M_sfa * K_sfa, BLK))](
+ sfa_gpu, sfa_blocked, M_sfa, K_sfa, l,
+ sfa_gpu.stride(0), sfa_gpu.stride(1), sfa_gpu.stride(2),
+ sfa_blocked.stride(0), sfa_blocked.stride(1),
+ BLK=BLK
+ )
+ blocked_transform_optimized[(l, ceil_div(M_sfb * K_sfb, BLK))](
+ sfb_gpu, sfb_blocked, M_sfb, K_sfb, l,
+ sfb_gpu.stride(0), sfb_gpu.stride(1), sfb_gpu.stride(2),
+ sfb_blocked.stride(0), sfb_blocked.stride(1),
+ BLK=BLK
+ )
+
+ # Main compute loop (still sequential, but tight)
+ for i in range(l):
+ res = torch._scaled_mm(
+ a[:, :, i],
+ b[:, :, i].transpose(0, 1),
+ sfa_blocked[i],
+ sfb_blocked[i],
+ bias=None,
+ out_dtype=torch.float16,
+ )
+ c[:, 0, i] = res[:, 0]
+
+ return c
+
+ # Entry point - use best approach
+ def custom_kernel(data: input_t) -> output_t:
+ """
+ Main entry - chooses best approach based on problem size.
+ For now, use fused transformation (Approach 3).
+ """
+ return custom_kernel_fused(data)
No newline at end of file
scrolls · 270 diff lines total

Best evidence level for this revision: reported

JSON