submission 102316
Raine · python · License unknown
Use it
Vendorable · source mirrored · license unknownView source →
No package. Vendor the mirrored source: 174 lines, June 9 Researcher Reciprocity License v1.0.
prefixsum_optimized.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-prefixsum-v2-102316?include=source"interfacepython
Compatibility
measured onNVIDIA L4
declared hardwareNVIDIA L4
architecturessm_89
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:dedf5dfe47f90fa222bc6542aef980166f0646dc575f93da144ab4cab051cbfe
license declaredunknown
license concludedunknown
authorsRaine
imported2026-08-15
Kernel source
prefixsum_optimized.py174 lines
import os
# Unset CUDA_VISIBLE_DEVICES before importing torch/triton
if 'CUDA_VISIBLE_DEVICES' in os.environ:
del os.environ['CUDA_VISIBLE_DEVICES']
import torch
import triton
import triton.language as tl
@triton.jit
def prefixsum_kernel(
input_ptr,
output_ptr,
n_elements,
BLOCK_SIZE: tl.constexpr,
):
"""Compute prefix sum within each block."""
pid = tl.program_id(axis=0)
block_start = pid * BLOCK_SIZE
offsets = block_start + tl.arange(0, BLOCK_SIZE)
mask = offsets < n_elements
# Load data from input
x = tl.load(input_ptr + offsets, mask=mask, other=0.0)
# Compute prefix sum within block
x = tl.cumsum(x, axis=0)
# Store the result
tl.store(output_ptr + offsets, x, mask=mask)
@triton.jit
def extract_block_sums_kernel(
data_ptr,
block_sums_ptr,
n_elements,
BLOCK_SIZE: tl.constexpr,
):
"""Extract the last element of each block (the block sum)."""
pid = tl.program_id(axis=0)
# Last index in this block
last_idx = min((pid + 1) * BLOCK_SIZE - 1, n_elements - 1)
# Load the last element (sum of entire block)
block_sum = tl.load(data_ptr + last_idx)
# Store to block_sums array
tl.store(block_sums_ptr + pid, block_sum)
@triton.jit
def add_block_offsets_kernel(
output_ptr,
block_offsets_ptr,
n_elements,
BLOCK_SIZE: tl.constexpr,
):
"""Add the offset from previous blocks to each element."""
pid = tl.program_id(axis=0)
if pid == 0:
return # First block has no offset
block_start = pid * BLOCK_SIZE
offsets = block_start + tl.arange(0, BLOCK_SIZE)
mask = offsets < n_elements
# Load the cumulative offset for this block
offset = tl.load(block_offsets_ptr + pid - 1)
# Load current values, add offset, and store back
x = tl.load(output_ptr + offsets, mask=mask)
x = x + offset
tl.store(output_ptr + offsets, x, mask=mask)
def prefixsum_recursive(input_tensor, output_tensor, block_size=1024):
"""
Recursive GPU-based prefix sum.
Args:
input_tensor: Input tensor (1D)
output_tensor: Output tensor (1D, same size as input)
block_size: Size of each block for parallel processing
Returns:
output_tensor: Result of prefix sum
"""
n_elements = input_tensor.numel()
num_blocks = triton.cdiv(n_elements, block_size)
# Base case: single block
if num_blocks == 1:
grid = (1,)
prefixsum_kernel[grid](
input_tensor,
output_tensor,
n_elements,
BLOCK_SIZE=block_size,
)
return output_tensor
# Recursive case: multiple blocks
grid = (num_blocks,)
# Step 1: Compute prefix sum within each block
prefixsum_kernel[grid](
input_tensor,
output_tensor,
n_elements,
BLOCK_SIZE=block_size,
)
# Step 2: Extract block sums (last element of each block)
block_sums = torch.empty(num_blocks, dtype=input_tensor.dtype, device=input_tensor.device)
extract_block_sums_kernel[grid](
output_tensor,
block_sums,
n_elements,
BLOCK_SIZE=block_size,
)
# Step 3: Recursively compute prefix sum of block sums
block_offsets = torch.empty_like(block_sums)
prefixsum_recursive(block_sums, block_offsets, block_size=block_size)
# Step 4: Add block offsets to all elements
add_block_offsets_kernel[grid](
output_tensor,
block_offsets,
n_elements,
BLOCK_SIZE=block_size,
)
return output_tensor
def custom_kernel(data, block_size=1024):
"""
Optimized prefix sum using fully GPU-based recursive algorithm.
Args:
data: Tuple of (input_tensor, output_tensor)
block_size: Block size for processing (default 1024)
Larger blocks = fewer recursion levels but less parallelism per block
Smaller blocks = more recursion but better intra-block parallelism
Returns:
output_tensor: Result of prefix sum
"""
input_tensor, output_tensor = data
# Auto-tune block size based on input size if desired
n = input_tensor.numel()
if block_size == 'auto':
# Heuristic: use larger blocks for larger inputs
if n < 1024:
block_size = 256
elif n < 16384:
block_size = 1024
elif n < 262144:
block_size = 2048
else:
block_size = 4096
return prefixsum_recursive(input_tensor, output_tensor, block_size=block_size)
# Backward compatibility
prefixsum = custom_kernel
scrolls · 174 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 102250.
import os# Unset CUDA_VISIBLE_DEVICES before importing torch/triton- # This works around SLURM setting it to a value that prevents PyTorch from seeing GPUsif 'CUDA_VISIBLE_DEVICES' in os.environ:del os.environ['CUDA_VISIBLE_DEVICES']- import triton.testing- import math- import logging-+ import torchimport tritonimport triton.language as tl- import torch- torch.cuda.empty_cache()- print(f"CUDA available: {torch.cuda.is_available()}")- print(f"Device count: {torch.cuda.device_count()}")- import torch.nn.functional as F@triton.jit- def naive_prefixsum_kernel(+ def prefixsum_kernel(input_ptr,output_ptr,n_elements,BLOCK_SIZE: tl.constexpr,):+ """Compute prefix sum within each block."""pid = tl.program_id(axis=0)block_start = pid * BLOCK_SIZEoffsets = block_start + tl.arange(0, BLOCK_SIZE)mask = offsets < n_elements- # Load input+ # Load data from inputx = tl.load(input_ptr + offsets, mask=mask, other=0.0)- # Single-thread accumulator- # Only lane 0 computes the prefix sum sequentially.- acc = tl.zeros((BLOCK_SIZE,), dtype=x.dtype)+ # Compute prefix sum within block+ x = tl.cumsum(x, axis=0)- # get the lane id within the program- lane_id = tl.arange(0, BLOCK_SIZE)+ # Store the result+ tl.store(output_ptr + offsets, x, mask=mask)- # Run prefix sum sequentially on lane 0- if tl.program_id(axis=1) == 0: # always true (1D grid) but needed for Triton scoping- # Only lane 0 executes the loop- if lane_id == 0:- running = 0.0- for i in range(BLOCK_SIZE):- running += x[i]- acc[i] = running- # Broadcast results: each lane loads its own index from acc- out = acc[lane_id]-- tl.store(output_ptr + offsets, out, mask=mask)----- # --------- kernel ----------------@triton.jit- def prefixsum_kernel(- input_ptr, # pointer to input array- output_ptr, # pointer to output array- n_elements, # total number of elements- BLOCK_SIZE: tl.constexpr, # block size for processing+ def extract_block_sums_kernel(+ data_ptr,+ block_sums_ptr,+ n_elements,+ BLOCK_SIZE: tl.constexpr,):+ """Extract the last element of each block (the block sum)."""pid = tl.program_id(axis=0)- block_start = pid * BLOCK_SIZE- offsets = block_start + tl.arange(0, BLOCK_SIZE)- mask = offsets < n_elements- # Load data from input, with padding for elements out of bounds- x = tl.load(input_ptr + offsets, mask=mask, other=0.0)+ # Last index in this block+ last_idx = min((pid + 1) * BLOCK_SIZE - 1, n_elements - 1)- # Perform parallel prefix sum within the block using tl.cumsum.- # cumsum is the built-in prefix sum operation in Triton.- x = tl.cumsum(x, axis=0)+ # Load the last element (sum of entire block)+ block_sum = tl.load(data_ptr + last_idx)- # Store the result to output- tl.store(output_ptr + offsets, x, mask=mask)+ # Store to block_sums array+ tl.store(block_sums_ptr + pid, block_sum)+@triton.jit- def add_block_sums_kernel(+ def add_block_offsets_kernel(output_ptr,- block_sums_ptr,+ block_offsets_ptr,n_elements,BLOCK_SIZE: tl.constexpr,):- """Add the cumulative sum from previous blocks to each element."""+ """Add the offset from previous blocks to each element."""pid = tl.program_id(axis=0)-+if pid == 0:- return # First block doesn't need adjustment-+ return # First block has no offset+block_start = pid * BLOCK_SIZEoffsets = block_start + tl.arange(0, BLOCK_SIZE)mask = offsets < n_elements-- # Load the cumulative sum from all previous blocks- prev_sum = tl.load(block_sums_ptr + pid - 1)-- # Load current values, add previous sum, and store back++ # Load the cumulative offset for this block+ offset = tl.load(block_offsets_ptr + pid - 1)++ # Load current values, add offset, and store backx = tl.load(output_ptr + offsets, mask=mask)- x = x + prev_sum+ x = x + offsettl.store(output_ptr + offsets, x, mask=mask)- # --------- wrapper function ----------------- def prefixsum(data):+ def prefixsum_recursive(input_tensor, output_tensor, block_size=1024):"""- Wrapper function to launch the prefix sum kernel.+ Recursive GPU-based prefix sum.+Args:- data: Tuple of (input_tensor, output_tensor)+ input_tensor: Input tensor (1D)+ output_tensor: Output tensor (1D, same size as input)+ block_size: Size of each block for parallel processing+Returns:- output_tensor: Tensor containing the inclusive prefix sum+ output_tensor: Result of prefix sum"""- input_tensor, output_tensor = datan_elements = input_tensor.numel()-- # Choose block size- BLOCK_SIZE = 4096-- # Calculate grid size- num_blocks = triton.cdiv(n_elements, BLOCK_SIZE)+ num_blocks = triton.cdiv(n_elements, block_size)++ # Base case: single block+ if num_blocks == 1:+ grid = (1,)+ prefixsum_kernel[grid](+ input_tensor,+ output_tensor,+ n_elements,+ BLOCK_SIZE=block_size,+ )+ return output_tensor++ # Recursive case: multiple blocksgrid = (num_blocks,)-- # Pass 1: Compute prefix sum within each block++ # Step 1: Compute prefix sum within each blockprefixsum_kernel[grid](input_tensor,output_tensor,n_elements,- BLOCK_SIZE=BLOCK_SIZE,+ BLOCK_SIZE=block_size,)- # If we have multiple blocks, we need to propagate sums across blocks- if num_blocks > 1:- # Extract the last element of each block (the block sums)- block_sums = torch.zeros(num_blocks, dtype=input_tensor.dtype, device=input_tensor.device)- for i in range(num_blocks):- last_idx = min((i + 1) * BLOCK_SIZE - 1, n_elements - 1)- block_sums[i] = output_tensor[last_idx]+ # Step 2: Extract block sums (last element of each block)+ block_sums = torch.empty(num_blocks, dtype=input_tensor.dtype, device=input_tensor.device)+ extract_block_sums_kernel[grid](+ output_tensor,+ block_sums,+ n_elements,+ BLOCK_SIZE=block_size,+ )- # Compute prefix sum of block sums- block_prefix = torch.cumsum(block_sums, dim=0)+ # Step 3: Recursively compute prefix sum of block sums+ block_offsets = torch.empty_like(block_sums)+ prefixsum_recursive(block_sums, block_offsets, block_size=block_size)- # Pass 2: Add accumulated sums from previous blocks- add_block_sums_kernel[grid](- output_tensor,- block_prefix,- n_elements,- BLOCK_SIZE=BLOCK_SIZE,- )+ # Step 4: Add block offsets to all elements+ add_block_offsets_kernel[grid](+ output_tensor,+ block_offsets,+ n_elements,+ BLOCK_SIZE=block_size,+ )return output_tensor- # Alias for submission system- custom_kernel = prefixsumNo newline at end of file+ def custom_kernel(data, block_size=1024):+ """+ Optimized prefix sum using fully GPU-based recursive algorithm.++ Args:+ data: Tuple of (input_tensor, output_tensor)+ block_size: Block size for processing (default 1024)+ Larger blocks = fewer recursion levels but less parallelism per block+ Smaller blocks = more recursion but better intra-block parallelism++ Returns:+ output_tensor: Result of prefix sum+ """+ input_tensor, output_tensor = data++ # Auto-tune block size based on input size if desired+ n = input_tensor.numel()+ if block_size == 'auto':+ # Heuristic: use larger blocks for larger inputs+ if n < 1024:+ block_size = 256+ elif n < 16384:+ block_size = 1024+ elif n < 262144:+ block_size = 2048+ else:+ block_size = 4096++ return prefixsum_recursive(input_tensor, output_tensor, block_size=block_size)+++ # Backward compatibility+ prefixsum = custom_kernel
scrolls · 268 diff lines total
Best evidence level for this revision: reported
JSON