submission 757709
Zeyu Li · python · License unknown
Use it
Vendorable · source mirrored · license unknownView source →
No package. Vendor the mirrored source: 145 lines, June 9 Researcher Reciprocity License v1.0.
submission.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-prefixsum-v2-757709?include=source"interfacepython
Compatibility
measured onNVIDIA H100
declared hardwareNVIDIA H100
architecturessm_90
dtypesfp32
Benchmark evidence
1 measurement across 1 GPU, fastest first.
Operation / workload
Hardware
Latency
Rank
Observed
Reported · How evidence levels are derived →
Source and license
sourceavailable
revision digestsha256:da941d4b41261075ff0e1c6bf5f2f8c8bde7acf8e0dc994051a9043c40fdad04
license declaredunknown
license concludedunknown
authorsZeyu Li
imported2026-08-15
Techniques
Extracted from the mirrored source by pattern, never inferred. Each row cites its line.
stages = 2
num_stages=2,Kernel source
submission.py145 lines
# EVOLVE-BLOCK-START
import torch
import triton
import triton.language as tl
from typing import Tuple
# ------------------------------------------------------------------
# Kernel: compute per‑block sums
# ------------------------------------------------------------------
@triton.jit
def block_sum_kernel(
x_ptr, # *float32, input vector
block_sum_ptr, # *float32, per‑block sums (output)
N, # int32, total number of elements
BLOCK_SIZE: tl.constexpr,
):
pid = tl.program_id(0) # block index
block_start = pid * BLOCK_SIZE
offsets = block_start + tl.arange(0, BLOCK_SIZE)
mask = offsets < N
# Load a tile (zero‑pad out‑of‑bounds)
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
# Reduce across the block
block_sum = tl.sum(x)
# Store the block sum
tl.store(block_sum_ptr + pid, block_sum)
# ------------------------------------------------------------------
# Kernel: final inclusive scan using per‑block exclusive offsets
# ------------------------------------------------------------------
@triton.jit
def final_scan_kernel(
x_ptr, # *float32, input vector
out_ptr, # *float32, output vector (final result)
block_offsets_ptr, # *float32, inclusive per‑block offsets
block_sums_ptr, # *float32, per‑block sums
N, # int32, total number of elements
BLOCK_SIZE: tl.constexpr,
):
pid = tl.program_id(0) # block index
block_start = pid * BLOCK_SIZE
offsets = block_start + tl.arange(0, BLOCK_SIZE)
mask = offsets < N
# Load input tile
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
# Inclusive prefix sum inside the block
y = tl.cumsum(x)
# Load inclusive offset for this block and its block sum
offset_incl = tl.load(block_offsets_ptr + pid)
block_sum = tl.load(block_sums_ptr + pid)
# Convert inclusive offset to exclusive offset
offset_excl = offset_incl - block_sum
# Apply the exclusive offset
y = y + offset_excl
# Write results
tl.store(out_ptr + offsets, y, mask=mask)
# ------------------------------------------------------------------
# Kernel: single‑pass scan for very small inputs (N <= BLOCK_SIZE)
# ------------------------------------------------------------------
@triton.jit
def small_scan_kernel(
x_ptr, # *float32, input vector
out_ptr, # *float32, output vector
N, # int32, total number of elements
BLOCK_SIZE: tl.constexpr,
):
offsets = tl.arange(0, BLOCK_SIZE)
mask = offsets < N
x = tl.load(x_ptr + offsets, mask=mask, other=0.0)
y = tl.cumsum(x)
tl.store(out_ptr + offsets, y, mask=mask)
# ------------------------------------------------------------------
# Public interface
# ------------------------------------------------------------------
def custom_kernel(data: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor:
"""
Inclusive prefix sum (scan) over a 1‑D float32 tensor using Triton.
"""
x, output = data
# Basic sanity checks
assert x.is_contiguous() and output.is_contiguous(), "Both tensors must be contiguous"
assert x.dtype == torch.float32 and output.dtype == torch.float32, "Only float32 supported"
assert x.shape == output.shape, "Input and output must have the same shape"
N = x.shape[0]
if N == 0:
output.zero_()
return output
# ------------------------------------------------------------------
# Configuration (tuned for Hopper/H200)
# ------------------------------------------------------------------
BLOCK_SIZE = 1024 # max threads per block
num_blocks = (N + BLOCK_SIZE - 1) // BLOCK_SIZE
# ------------------------------------------------------------------
# Small‑input fast path
# ------------------------------------------------------------------
if N <= BLOCK_SIZE:
small_scan_kernel[(1,)](
x, output, N,
BLOCK_SIZE=BLOCK_SIZE,
num_stages=2,
)
return output
# ------------------------------------------------------------------
# Large‑input hierarchical scan
# ------------------------------------------------------------------
# 1️⃣ Compute per‑block sums
block_sums = torch.empty(num_blocks, dtype=x.dtype, device=x.device)
block_sum_kernel[(num_blocks,)](
x, block_sums, N,
BLOCK_SIZE=BLOCK_SIZE,
num_stages=2,
)
# 2️⃣ Compute inclusive block offsets on the GPU
block_offsets = torch.empty_like(block_sums)
torch.cumsum(block_sums, dim=0, out=block_offsets)
# 3️⃣ Final scan with exclusive offsets
final_scan_kernel[(num_blocks,)](
x, output, block_offsets, block_sums, N,
BLOCK_SIZE=BLOCK_SIZE,
num_stages=4,
)
return output
# EVOLVE-BLOCK-ENDscrolls · 145 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 757213.
⋯ 4 unchanged linesimport triton.language as tlfrom typing import Tuple- # ----------------------------------------------------------------------- # Combine function for associative scan (simple addition)- # ----------------------------------------------------------------------+ # ------------------------------------------------------------------+ # Kernel: compute per‑block sums+ # ------------------------------------------------------------------@triton.jit- def _add_combine(a, b):- return a + b-- # ----------------------------------------------------------------------- # Kernel 1: compute per‑block sums of the input tensor- # ----------------------------------------------------------------------- @triton.jitdef block_sum_kernel(- x_ptr, # *Pointer* to input tensor- block_sum_ptr, # *Pointer* to per‑block sum buffer- n: tl.int64, # total number of elements+ x_ptr, # *float32, input vector+ block_sum_ptr, # *float32, per‑block sums (output)+ N, # int32, total number of elementsBLOCK_SIZE: tl.constexpr,):pid = tl.program_id(0) # block indexblock_start = pid * BLOCK_SIZEoffsets = block_start + tl.arange(0, BLOCK_SIZE)- mask = offsets < n+ mask = offsets < N- # Load a chunk of the input (zero‑pad out‑of‑bounds)+ # Load a tile (zero‑pad out‑of‑bounds)x = tl.load(x_ptr + offsets, mask=mask, other=0.0)- # Reduce to a single sum for this block+ # Reduce across the blockblock_sum = tl.sum(x)- # Write the per‑block sum+ # Store the block sumtl.store(block_sum_ptr + pid, block_sum)- # ----------------------------------------------------------------------- # Kernel 2: final inclusive scan + add exclusive per‑block offset- # ----------------------------------------------------------------------++ # ------------------------------------------------------------------+ # Kernel: final inclusive scan using per‑block exclusive offsets+ # ------------------------------------------------------------------@triton.jitdef final_scan_kernel(- x_ptr, # *Pointer* to input tensor- out_ptr, # *Pointer* to output tensor- block_sum_ptr, # *Pointer* to per‑block sum buffer (original)- block_sum_scan_ptr, # *Pointer* to inclusive scan of per‑block sums- n: tl.int64, # total number of elements+ x_ptr, # *float32, input vector+ out_ptr, # *float32, output vector (final result)+ block_offsets_ptr, # *float32, inclusive per‑block offsets+ block_sums_ptr, # *float32, per‑block sums+ N, # int32, total number of elementsBLOCK_SIZE: tl.constexpr,):pid = tl.program_id(0) # block indexblock_start = pid * BLOCK_SIZEoffsets = block_start + tl.arange(0, BLOCK_SIZE)- mask = offsets < n+ mask = offsets < N- # Load the input slice for this block+ # Load input tilex = tl.load(x_ptr + offsets, mask=mask, other=0.0)- # Inclusive scan within the block- local_scan = tl.associative_scan(x, axis=0, combine_fn=_add_combine)+ # Inclusive prefix sum inside the block+ y = tl.cumsum(x)- # Load per‑block sums (original) and its inclusive scan- block_sum = tl.load(block_sum_ptr + pid)- block_sum_scan = tl.load(block_sum_scan_ptr + pid)+ # Load inclusive offset for this block and its block sum+ offset_incl = tl.load(block_offsets_ptr + pid)+ block_sum = tl.load(block_sums_ptr + pid)- # Exclusive offset = sum of all previous blocks- offset = block_sum_scan - block_sum+ # Convert inclusive offset to exclusive offset+ offset_excl = offset_incl - block_sum- # Apply the offset to the locally scanned values- out = local_scan + offset+ # Apply the exclusive offset+ y = y + offset_excl- # Write the final result- tl.store(out_ptr + offsets, out, mask=mask)+ # Write results+ tl.store(out_ptr + offsets, y, mask=mask)- # ----------------------------------------------------------------------- # Public API- # ----------------------------------------------------------------------++ # ------------------------------------------------------------------+ # Kernel: single‑pass scan for very small inputs (N <= BLOCK_SIZE)+ # ------------------------------------------------------------------+ @triton.jit+ def small_scan_kernel(+ x_ptr, # *float32, input vector+ out_ptr, # *float32, output vector+ N, # int32, total number of elements+ BLOCK_SIZE: tl.constexpr,+ ):+ offsets = tl.arange(0, BLOCK_SIZE)+ mask = offsets < N+ x = tl.load(x_ptr + offsets, mask=mask, other=0.0)+ y = tl.cumsum(x)+ tl.store(out_ptr + offsets, y, mask=mask)+++ # ------------------------------------------------------------------+ # Public interface+ # ------------------------------------------------------------------def custom_kernel(data: Tuple[torch.Tensor, torch.Tensor]) -> torch.Tensor:"""- Inclusive prefix sum (cumsum) over a 1‑D float32 tensor using a two‑pass- Triton implementation with a small‑size fallback to torch.cumsum.+ Inclusive prefix sum (scan) over a 1‑D float32 tensor using Triton."""x, output = data- # ------------------------------------------------------------------- # Validation- # ------------------------------------------------------------------- if not (x.is_cuda and output.is_cuda):- raise RuntimeError("custom_kernel expects CUDA tensors")- if x.dtype != torch.float32 or output.dtype != torch.float32:- raise RuntimeError("custom_kernel only supports float32 tensors")- if x.shape != output.shape:- raise ValueError("input and output must have the same shape")- if x.ndim != 1:- raise ValueError("custom_kernel only supports 1‑D tensors")+ # Basic sanity checks+ assert x.is_contiguous() and output.is_contiguous(), "Both tensors must be contiguous"+ assert x.dtype == torch.float32 and output.dtype == torch.float32, "Only float32 supported"+ assert x.shape == output.shape, "Input and output must have the same shape"- n = x.numel()- if n == 0:+ N = x.shape[0]+ if N == 0:+ output.zero_()return output# ------------------------------------------------------------------- # Small‑tensor fast path using native torch.cumsum+ # Configuration (tuned for Hopper/H200)# ------------------------------------------------------------------- SMALL_THRESHOLD = 1 << 20 # 1,048,576 elements- if n <= SMALL_THRESHOLD:- torch.cumsum(x, dim=0, out=output)- return output+ BLOCK_SIZE = 1024 # max threads per block+ num_blocks = (N + BLOCK_SIZE - 1) // BLOCK_SIZE# ------------------------------------------------------------------- # Triton launch configuration+ # Small‑input fast path# ------------------------------------------------------------------- BLOCK_SIZE = 32768 # elements per block (multiple of 32)- NUM_WARPS_SUM = 8 # 8 warps → 256 threads per block (reduction)- NUM_WARPS_SCAN = 8 # 8 warps → 256 threads per block (scan)- num_blocks = (n + BLOCK_SIZE - 1) // BLOCK_SIZE+ if N <= BLOCK_SIZE:+ small_scan_kernel[(1,)](+ x, output, N,+ BLOCK_SIZE=BLOCK_SIZE,+ num_stages=2,+ )+ return output- # Buffer for per‑block sums- block_sums = torch.empty(num_blocks, dtype=x.dtype, device=x.device)-# ------------------------------------------------------------------- # Pass 1: compute per‑block sums+ # Large‑input hierarchical scan# ------------------------------------------------------------------+ # 1️⃣ Compute per‑block sums+ block_sums = torch.empty(num_blocks, dtype=x.dtype, device=x.device)block_sum_kernel[(num_blocks,)](- x,- block_sums,- n,+ x, block_sums, N,BLOCK_SIZE=BLOCK_SIZE,- num_warps=NUM_WARPS_SUM,- num_stages=3, # modest pipelining for the reduction kernel+ num_stages=2,)- # ------------------------------------------------------------------- # Scan the per‑block totals on the GPU (inclusive)- # ------------------------------------------------------------------- block_sums_scan = torch.cumsum(block_sums, dim=0)+ # 2️⃣ Compute inclusive block offsets on the GPU+ block_offsets = torch.empty_like(block_sums)+ torch.cumsum(block_sums, dim=0, out=block_offsets)- # ------------------------------------------------------------------- # Pass 2: local inclusive scan + add exclusive per‑block offsets- # ------------------------------------------------------------------+ # 3️⃣ Final scan with exclusive offsetsfinal_scan_kernel[(num_blocks,)](- x,- output,- block_sums,- block_sums_scan,- n,+ x, output, block_offsets, block_sums, N,BLOCK_SIZE=BLOCK_SIZE,- num_warps=NUM_WARPS_SCAN,- num_stages=6, # deeper pipelining to hide memory latency+ num_stages=4,)-return output# EVOLVE-BLOCK-ENDNo newline at end of file
scrolls · 233 diff lines total
Best evidence level for this revision: reported
JSON