Skip to content
KernelIndex
Search⌘K

submission 758463

Zeyu Li · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

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

submission.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-histogram-v2-758463?include=source"
interfacepython
Compatibility
measured onNVIDIA H100
declared hardwareNVIDIA H100
architecturessm_90
dtypesuint8

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
Histogramsuite of 6 cases
NVIDIA H100
157.4µs
#13 of 24
2026-04-09

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:6c33c0f6864dbc4b3a85db2ea7247c6e240e499d9d44c3db450b9f22b902e098
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 = 3num_stages=3,

Kernel source

submission.py115 lines
# EVOLVE-BLOCK-START

import torch
import triton
import triton.language as tl
from typing import TypeVar

input_t = TypeVar("input_t", bound=tuple[torch.Tensor, torch.Tensor])
output_t = TypeVar("output_t", bound=torch.Tensor)


@triton.jit
def histogram_warp_kernel(
    data_ptr,          # *uint8
    partial_ptr,       # *int32
    N: tl.int32,
    BLOCK_SIZE: tl.constexpr,
    CHUNK: tl.constexpr,
    WARP_SIZE: tl.constexpr,
):
    """Per‑block, per‑warp histogram kernel.

    Each block processes a contiguous ``CHUNK`` of the input.  Within a block
    we allocate ``NUM_WARPS`` independent 256‑bin histograms in global memory.
    Threads update the histogram belonging to their warp using atomic adds.
    """
    pid = tl.program_id(0)                # block index
    lane = tl.arange(0, BLOCK_SIZE)       # thread index within block

    # Number of warps per block (BLOCK_SIZE is a multiple of 32)
    NUM_WARPS = BLOCK_SIZE // WARP_SIZE

    # Base pointer for this block's per‑warp histograms (flattened layout)
    # Layout: [block, warp, bin]
    block_base = partial_ptr + pid * NUM_WARPS * 256

    # -----------------------------------------------------------------
    # Zero‑initialize each warp's histogram.
    # Every thread writes zero to its lane bin for every warp.
    for w in range(NUM_WARPS):
        tl.store(block_base + w * 256 + lane, 0)

    # Make sure the zero stores are visible before any atomics.
    tl.debug_barrier()

    # -----------------------------------------------------------------
    # Process a contiguous chunk of the input.
    block_start = pid * CHUNK
    ITEMS_PER_THREAD = CHUNK // BLOCK_SIZE   # compile‑time constant

    for i in range(ITEMS_PER_THREAD):
        offset = block_start + lane + i * BLOCK_SIZE
        mask = offset < N
        # Load a uint8 value (masked)
        val = tl.load(data_ptr + offset, mask=mask, other=0)
        # Convert to a 0‑255 index
        idx = tl.cast(val, tl.int32)
        # Identify the warp this thread belongs to
        warp_id = lane // WARP_SIZE
        # Pointer to the appropriate bin in this warp's histogram
        bin_ptr = block_base + warp_id * 256 + idx
        # Atomically increment the bin (masked)
        tl.atomic_add(bin_ptr, 1, mask=mask)


def custom_kernel(data: input_t) -> output_t:
    """
    Compute a 256‑bin histogram of a 1‑D uint8 tensor using a Triton kernel.
    The kernel builds per‑warp partial histograms in global memory and then
    reduces them with a fast Torch sum.
    """
    data_tensor, output = data
    N = data_tensor.shape[0]
    device = data_tensor.device

    # -----------------------------------------------------------------
    # Handle empty input early.
    if N == 0:
        output.zero_()
        return output

    # -----------------------------------------------------------------
    # Tunable launch parameters
    BLOCK_SIZE = 256            # one thread per histogram bin
    WARP_SIZE = 32
    NUM_WARPS = BLOCK_SIZE // WARP_SIZE
    # CHUNK: number of elements processed per block (must be multiple of BLOCK_SIZE)
    CHUNK = 16384               # 64 * BLOCK_SIZE

    # Number of blocks required to cover the input
    num_blocks = (N + CHUNK - 1) // CHUNK

    # Allocate per‑warp partial histograms (int32 is sufficient for counts)
    # Shape: [num_blocks, NUM_WARPS, 256]
    partial = torch.empty((num_blocks, NUM_WARPS, 256), dtype=torch.int32, device=device)

    # Launch the kernel
    histogram_warp_kernel[(num_blocks,)](
        data_tensor,
        partial,
        N,
        BLOCK_SIZE=BLOCK_SIZE,
        CHUNK=CHUNK,
        WARP_SIZE=WARP_SIZE,
        num_warps=NUM_WARPS,
        num_stages=3,
    )

    # Reduce across blocks and warps into the final 256‑bin histogram.
    # Cast to the output dtype (int64) during the reduction.
    output[:] = partial.sum(dim=(0, 1), dtype=output.dtype)

    return output
# EVOLVE-BLOCK-END
scrolls · 115 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 757228.

⋯ 9 unchanged lines
@triton.jit
- def histogram_kernel(
- data_ptr,
- tmp_hist_ptr,
- N,
+ def histogram_warp_kernel(
+ data_ptr, # *uint8
+ partial_ptr, # *int32
+ N: tl.int32,
BLOCK_SIZE: tl.constexpr,
CHUNK: tl.constexpr,
+ WARP_SIZE: tl.constexpr,
):
+ """Per‑block, per‑warp histogram kernel.
+
+ Each block processes a contiguous ``CHUNK`` of the input. Within a block
+ we allocate ``NUM_WARPS`` independent 256‑bin histograms in global memory.
+ Threads update the histogram belonging to their warp using atomic adds.
"""
- Build a per‑block 256‑bin histogram in a temporary global buffer.
- Each block writes to its own slice of `tmp_hist` (256 int32 entries).
- """
- pid = tl.program_id(0)
+ pid = tl.program_id(0) # block index
+ lane = tl.arange(0, BLOCK_SIZE) # thread index within block
- # Pointer to the slice belonging to this block (256 int32 bins)
- tmp_ptr = tmp_hist_ptr + pid * 256
+ # Number of warps per block (BLOCK_SIZE is a multiple of 32)
+ NUM_WARPS = BLOCK_SIZE // WARP_SIZE
- # Thread index inside the block
- thread_idx = tl.arange(0, BLOCK_SIZE)
+ # Base pointer for this block's per‑warp histograms (flattened layout)
+ # Layout: [block, warp, bin]
+ block_base = partial_ptr + pid * NUM_WARPS * 256
- # Base offset for this thread: each thread processes CHUNK elements spaced by BLOCK_SIZE
- base = pid * BLOCK_SIZE * CHUNK + thread_idx
+ # -----------------------------------------------------------------
+ # Zero‑initialize each warp's histogram.
+ # Every thread writes zero to its lane bin for every warp.
+ for w in range(NUM_WARPS):
+ tl.store(block_base + w * 256 + lane, 0)
- # Loop over the CHUNK elements assigned to this thread
- for i in range(CHUNK):
- idx = base + i * BLOCK_SIZE
- mask = idx < N
+ # Make sure the zero stores are visible before any atomics.
+ tl.debug_barrier()
- # Load a uint8 value (0‑255) and cast to int32 for indexing
- val = tl.load(data_ptr + idx, mask=mask, other=0).to(tl.int32)
+ # -----------------------------------------------------------------
+ # Process a contiguous chunk of the input.
+ block_start = pid * CHUNK
+ ITEMS_PER_THREAD = CHUNK // BLOCK_SIZE # compile‑time constant
- # Increment the per‑block histogram atomically
- tl.atomic_add(tmp_ptr + val, 1, mask=mask)
+ for i in range(ITEMS_PER_THREAD):
+ offset = block_start + lane + i * BLOCK_SIZE
+ mask = offset < N
+ # Load a uint8 value (masked)
+ val = tl.load(data_ptr + offset, mask=mask, other=0)
+ # Convert to a 0‑255 index
+ idx = tl.cast(val, tl.int32)
+ # Identify the warp this thread belongs to
+ warp_id = lane // WARP_SIZE
+ # Pointer to the appropriate bin in this warp's histogram
+ bin_ptr = block_base + warp_id * 256 + idx
+ # Atomically increment the bin (masked)
+ tl.atomic_add(bin_ptr, 1, mask=mask)
- @triton.jit
- def reduce_histogram_kernel(
- tmp_hist_ptr,
- out_ptr,
- BLOCK_SIZE: tl.constexpr,
- ):
- """
- Reduce all per‑block histograms into the final output.
- One thread per bin (0‑255) adds its bin count from this block to `out`.
- """
- pid = tl.program_id(0)
-
- # One thread per histogram bin
- bin_idx = tl.arange(0, BLOCK_SIZE)
- mask = bin_idx < 256
-
- # Load the count for this bin from this block's temporary histogram (int32 → int64)
- cnt = tl.load(tmp_hist_ptr + pid * 256 + bin_idx, mask=mask).to(tl.int64)
-
- # Atomically add to the final output (int64)
- tl.atomic_add(out_ptr + bin_idx, cnt, mask=mask)
-
-
def custom_kernel(data: input_t) -> output_t:
"""
- Compute a 256‑bin histogram of a uint8 1‑D tensor.
- This implementation builds per‑block sub‑histograms in a temporary buffer
- to dramatically reduce atomic contention, then reduces them into the final
- output.
+ Compute a 256‑bin histogram of a 1‑D uint8 tensor using a Triton kernel.
+ The kernel builds per‑warp partial histograms in global memory and then
+ reduces them with a fast Torch sum.
"""
- data_tensor, output_tensor = data
-
- # Ensure inputs are contiguous and on the same device
- data_tensor = data_tensor.contiguous()
- output_tensor = output_tensor.contiguous()
+ data_tensor, output = data
+ N = data_tensor.shape[0]
device = data_tensor.device
- # Zero the output tensor (required for atomic accumulation)
- output_tensor.zero_()
+ # -----------------------------------------------------------------
+ # Handle empty input early.
+ if N == 0:
+ output.zero_()
+ return output
- N = data_tensor.numel()
+ # -----------------------------------------------------------------
+ # Tunable launch parameters
+ BLOCK_SIZE = 256 # one thread per histogram bin
+ WARP_SIZE = 32
+ NUM_WARPS = BLOCK_SIZE // WARP_SIZE
+ # CHUNK: number of elements processed per block (must be multiple of BLOCK_SIZE)
+ CHUNK = 16384 # 64 * BLOCK_SIZE
- # Kernel launch configuration
- BLOCK_SIZE = 256 # one thread per histogram bin (256 threads)
- CHUNK = 16 # elements processed per thread
+ # Number of blocks required to cover the input
+ num_blocks = (N + CHUNK - 1) // CHUNK
- # Number of blocks needed to cover the input
- num_blocks = max(1, triton.cdiv(N, BLOCK_SIZE * CHUNK))
- grid = (num_blocks,)
+ # Allocate per‑warp partial histograms (int32 is sufficient for counts)
+ # Shape: [num_blocks, NUM_WARPS, 256]
+ partial = torch.empty((num_blocks, NUM_WARPS, 256), dtype=torch.int32, device=device)
- # Temporary buffer for per‑block histograms (int32)
- tmp_hist = torch.zeros((num_blocks, 256), dtype=torch.int32, device=device)
-
- # -----------------------------------------------------------------
- # Phase 1: build per‑block histograms
- histogram_kernel[grid](
+ # Launch the kernel
+ histogram_warp_kernel[(num_blocks,)](
data_tensor,
- tmp_hist,
+ partial,
N,
BLOCK_SIZE=BLOCK_SIZE,
CHUNK=CHUNK,
- num_warps=8,
- num_stages=4,
+ WARP_SIZE=WARP_SIZE,
+ num_warps=NUM_WARPS,
+ num_stages=3,
)
- # -----------------------------------------------------------------
- # Phase 2: reduce per‑block histograms into the final output
- reduce_histogram_kernel[grid](
- tmp_hist,
- output_tensor,
- BLOCK_SIZE=BLOCK_SIZE,
- num_warps=8,
- num_stages=2,
- )
- # -----------------------------------------------------------------
- return output_tensor
+ # Reduce across blocks and warps into the final 256‑bin histogram.
+ # Cast to the output dtype (int64) during the reduction.
+ output[:] = partial.sum(dim=(0, 1), dtype=output.dtype)
+
+ return output
# EVOLVE-BLOCK-END
scrolls · 189 diff lines total

Best evidence level for this revision: reported

JSON