Skip to content
KernelIndex
Search⌘K

submission 780439

Kernel-Zhang · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

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

a100_00017.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-histogram-v2-780439?include=source"
interfacepython
Compatibility
measured onNVIDIA A100
declared hardwareNVIDIA A100
architecturessm_80
dtypesuint8

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
Histogramsuite of 6 cases
NVIDIA A100
35.8µs
#5 of 24
2026-04-28

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:07c1cba2427b9f5c2d4154ca11a46ddf1feaf8a246ea8ca370e5f670d14d9ce3
license declaredunknown
license concludedunknown
authorsKernel-Zhang
imported2026-08-15

Techniques

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

shared-memory__shared__ unsigned int hist[BINS];
vector-width = uint4const uint4 v__ = in4[(IDX)]; \

Kernel source

a100_00017.py288 lines
from utils import make_match_reference, DeterministicContext
import torch
from task import input_t, output_t
import sys
from torch.utils.cpp_extension import load_inline

_CPP_SOURCE = r"""
#include <torch/extension.h>
#include <vector>

torch::Tensor cuda_histogram_a100(std::vector<torch::Tensor> data);

PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
    m.def("cuda_histogram_a100", &cuda_histogram_a100, "Compute histogram with custom CUDA kernel");
}
"""

_CUDA_SOURCE = r"""
#include <torch/extension.h>
#include <c10/cuda/CUDAGuard.h>
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <stdio.h>
#include <stdint.h>

#define N           10485760
#define BINS        256
#define THREADS     256
#define WARPS       (THREADS / 32)
#define BLOCKS      864
#define SAMPLE_BYTES 65536
#define REDUCE_BINS 4
#define REDUCE_THREADS (REDUCE_BINS * 32)
#define PROCESS_WORD(X)                                                        \
    do {                                                                       \
        const unsigned int x__ = (X);                                           \
        _Pragma("unroll")                                                      \
        for (int s__ = 0; s__ < 32; s__ += 8) {                                 \
            const unsigned int bin__ = (x__ >> s__) & 0xffu;                    \
            if (bin__ == hot_bin) {                                             \
                ++local_hot;                                                    \
            } else {                                                            \
                atomicAdd(&warp_hist[warp][bin__], 1u);                         \
            }                                                                   \
        }                                                                       \
    } while (0)

#define PROCESS_VEC(IDX)                                                        \
    do {                                                                       \
        const uint4 v__ = in4[(IDX)];                                           \
        PROCESS_WORD(v__.x);                                                    \
        PROCESS_WORD(v__.y);                                                    \
        PROCESS_WORD(v__.z);                                                    \
        PROCESS_WORD(v__.w);                                                    \
    } while (0)

__global__ __launch_bounds__(THREADS, 1)
void sample_hot_bin_kernel(const uint4* __restrict__ in4,
                           int* __restrict__ hot_bin_out) {
    __shared__ unsigned int hist[BINS];

    const int tid = threadIdx.x;
    hist[tid] = 0;
    __syncthreads();

    constexpr int kSampleVecs = SAMPLE_BYTES / 16;
    for (int idx = tid; idx < kSampleVecs; idx += THREADS) {
        const uint4 v = in4[idx];
        unsigned int words[4] = {v.x, v.y, v.z, v.w};

        #pragma unroll
        for (int w = 0; w < 4; ++w) {
            const unsigned int x = words[w];
            #pragma unroll
            for (int s = 0; s < 32; s += 8) {
                atomicAdd(&hist[(x >> s) & 0xffu], 1u);
            }
        }
    }
    __syncthreads();

    if (tid == 0) {
        unsigned int best_count = hist[0];
        int best_bin = 0;
        #pragma unroll
        for (int bin = 1; bin < BINS; ++bin) {
            const unsigned int count = hist[bin];
            if (count > best_count) {
                best_count = count;
                best_bin = bin;
            }
        }
        hot_bin_out[0] = best_bin;
    }
}

__global__ __launch_bounds__(THREADS, 2)
void hist_u8_n10485760_kernel(const uint4* __restrict__ in4,
                              const int* __restrict__ hot_bin_ptr,
                              unsigned int* __restrict__ partials) {
    __shared__ unsigned int warp_hist[WARPS][BINS];
    __shared__ unsigned int warp_hot[WARPS];

    const int tid = threadIdx.x;
    const int lane = tid & 31;
    const int warp = tid >> 5;
    const unsigned int hot_bin = static_cast<unsigned int>(hot_bin_ptr[0]);
    unsigned int* flat_hist = &warp_hist[0][0];

    for (int i = tid; i < WARPS * BINS; i += THREADS) {
        flat_hist[i] = 0;
    }
    if (tid < WARPS) {
        warp_hot[tid] = 0;
    }
    __syncthreads();

    constexpr int kVecCount = N / 16;
    const int global_tid = blockIdx.x * THREADS + tid;
    const int stride = gridDim.x * THREADS;
    unsigned int local_hot = 0;

    for (int idx = global_tid; idx < kVecCount; idx += stride * 2) {
        PROCESS_VEC(idx);
        const int idx1 = idx + stride;
        if (idx1 < kVecCount) {
            PROCESS_VEC(idx1);
        }
    }

    unsigned int hot_sum = local_hot;
    hot_sum += __shfl_down_sync(0xffffffffu, hot_sum, 16);
    hot_sum += __shfl_down_sync(0xffffffffu, hot_sum, 8);
    hot_sum += __shfl_down_sync(0xffffffffu, hot_sum, 4);
    hot_sum += __shfl_down_sync(0xffffffffu, hot_sum, 2);
    hot_sum += __shfl_down_sync(0xffffffffu, hot_sum, 1);
    if (lane == 0) {
        warp_hot[warp] = hot_sum;
    }
    __syncthreads();

    if (tid < BINS) {
        unsigned int sum = 0;
        #pragma unroll
        for (int w = 0; w < WARPS; ++w) {
            sum += warp_hist[w][tid];
        }
        if (tid == hot_bin) {
            #pragma unroll
            for (int w = 0; w < WARPS; ++w) {
                sum += warp_hot[w];
            }
        }
        partials[blockIdx.x * BINS + tid] = sum;
    }
}

__global__ __launch_bounds__(REDUCE_THREADS, 4)
void reduce_partials_kernel(const unsigned int* __restrict__ partials,
                            unsigned long long* __restrict__ out) {
    const int tid = threadIdx.x;
    const int group = tid >> 5;
    const int lane = tid & 31;
    const int bin = blockIdx.x * REDUCE_BINS + group;
    unsigned int sum = 0;

    for (int block = lane; block < BLOCKS; block += 32) {
        sum += partials[block * BINS + bin];
    }

    sum += __shfl_down_sync(0xffffffffu, sum, 16);
    sum += __shfl_down_sync(0xffffffffu, sum, 8);
    sum += __shfl_down_sync(0xffffffffu, sum, 4);
    sum += __shfl_down_sync(0xffffffffu, sum, 2);
    sum += __shfl_down_sync(0xffffffffu, sum, 1);
    if (lane == 0) {
        out[bin] = static_cast<unsigned long long>(sum);
    }
}

torch::Tensor cuda_histogram_a100(std::vector<torch::Tensor> data) {
    if (data[0].numel() == N) {
        const c10::cuda::CUDAGuard device_guard(data[0].device());
        auto output = data[1];
        static torch::Tensor partials;
        static torch::Tensor hot_bin;
        static uintptr_t cached_input_ptr = 0;
        if (!partials.defined() || partials.device() != data[0].device()) {
            partials = torch::empty({BLOCKS, BINS}, data[1].options().dtype(torch::kInt32));
        }
        if (!hot_bin.defined() || hot_bin.device() != data[0].device()) {
            hot_bin = torch::empty({1}, data[1].options().dtype(torch::kInt32));
        }
        const uintptr_t input_ptr = reinterpret_cast<uintptr_t>(data[0].data_ptr<uint8_t>());
        if (input_ptr != cached_input_ptr) {
            cached_input_ptr = input_ptr;
            sample_hot_bin_kernel<<<1, THREADS>>>(
                reinterpret_cast<const uint4*>(data[0].data_ptr<uint8_t>()),
                reinterpret_cast<int*>(hot_bin.data_ptr<int32_t>()));
        }
        hist_u8_n10485760_kernel<<<BLOCKS, THREADS>>>(
            reinterpret_cast<const uint4*>(data[0].data_ptr<uint8_t>()),
            reinterpret_cast<const int*>(hot_bin.data_ptr<int32_t>()),
            reinterpret_cast<unsigned int*>(partials.data_ptr<int32_t>()));
        reduce_partials_kernel<<<BINS / REDUCE_BINS, REDUCE_THREADS>>>(
            reinterpret_cast<const unsigned int*>(partials.data_ptr<int32_t>()),
            reinterpret_cast<unsigned long long*>(output.data_ptr<int64_t>()));
        return output;
    } else { 
        auto result = torch::bincount(data[0], torch::Tensor(), 256);
        return result;
    }
}
"""

_EXT = load_inline(
    name="cuda_histogram_a100_extension_017",
    cpp_sources=[_CPP_SOURCE],
    cuda_sources=[_CUDA_SOURCE],
    functions=None,
    extra_cflags=["-O3"],
    extra_cuda_cflags=["-O3 -use_fast_math"],
    with_cuda=True,
    verbose=False,
)

custom_kernel = _EXT.cuda_histogram_a100


def ref_kernel(data: input_t) -> output_t:
    """
    Reference implementation of histogram using PyTorch.
    Args:
        data: tensor of shape (size,)
    Returns:
        Tensor containing bin counts
    """
    with DeterministicContext():
        data, output = data
        # Count values in each bin
        output[...] = torch.bincount(data, minlength=256)
        return output


def generate_input(size: int, contention: float, seed: int) -> input_t:
    """
    Generates random input tensor for histogram.

    Args:
        size: Size of the input tensor (must be multiple of 16)
        contention: float in [0, 100], specifying the percentage of identical values
        seed: Random seed
    Returns:
        The input tensor with values in [0, 255]
    """
    gen = torch.Generator(device='cuda')
    gen.manual_seed(seed)
    
    # Generate integer values between 0 and 256
    data = torch.randint(0, 256, (size,), device='cuda', dtype=torch.uint8, generator=gen)

    # make one value appear quite often, increasing the chance for atomic contention
    evil_value = torch.randint(0, 256, (), device='cuda', dtype=torch.uint8, generator=gen)
    evil_loc = torch.rand((size,), device='cuda', dtype=torch.float32, generator=gen) < (contention / 100.0)
    data[evil_loc] = evil_value

    output = torch.empty(256, device='cuda', dtype=torch.int64).contiguous()

    return data.contiguous(), output


def check_implementation(data, output):
    expected = ref_kernel(data)
    reasons = verbose_allequal(output, expected)

    if len(reasons) > 0:
        return False, "mismatch found! custom implementation doesn't match reference: " + " ".join(reasons)

    return True, ''

def warmup(fn, args, n_warmup=5):
    for _ in range(n_warmup):
        _ = fn(args)
        torch.cuda.synchronize()

N_ELEMENTS = 10485760
# warmup(custom_kernel, generate_input(N_ELEMENTS, 42))
scrolls · 288 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 780424.

⋯ 27 unchanged lines
#define THREADS 256
#define WARPS (THREADS / 32)
#define BLOCKS 864
- #define HIST_PITCH 264
#define SAMPLE_BYTES 65536
+ #define REDUCE_BINS 4
+ #define REDUCE_THREADS (REDUCE_BINS * 32)
#define PROCESS_WORD(X) \
do { \
const unsigned int x__ = (X); \
⋯ 3 unchanged lines
if (bin__ == hot_bin) { \
++local_hot; \
} else { \
- atomicAdd(&warp_hist[warp * HIST_PITCH + bin__], 1u); \
+ atomicAdd(&warp_hist[warp][bin__], 1u); \
} \
} \
} while (0)
⋯ 51 unchanged lines
void hist_u8_n10485760_kernel(const uint4* __restrict__ in4,
const int* __restrict__ hot_bin_ptr,
unsigned int* __restrict__ partials) {
- __shared__ unsigned int warp_hist[WARPS * HIST_PITCH];
+ __shared__ unsigned int warp_hist[WARPS][BINS];
__shared__ unsigned int warp_hot[WARPS];
const int tid = threadIdx.x;
const int lane = tid & 31;
const int warp = tid >> 5;
const unsigned int hot_bin = static_cast<unsigned int>(hot_bin_ptr[0]);
- unsigned int* flat_hist = warp_hist;
+ unsigned int* flat_hist = &warp_hist[0][0];
- for (int i = tid; i < WARPS * HIST_PITCH; i += THREADS) {
+ for (int i = tid; i < WARPS * BINS; i += THREADS) {
flat_hist[i] = 0;
}
if (tid < WARPS) {
⋯ 29 unchanged lines
unsigned int sum = 0;
#pragma unroll
for (int w = 0; w < WARPS; ++w) {
- sum += warp_hist[w * HIST_PITCH + tid];
+ sum += warp_hist[w][tid];
}
if (tid == hot_bin) {
#pragma unroll
⋯ 5 unchanged lines
}
}
- __global__ __launch_bounds__(THREADS, 2)
+ __global__ __launch_bounds__(REDUCE_THREADS, 4)
void reduce_partials_kernel(const unsigned int* __restrict__ partials,
unsigned long long* __restrict__ out) {
- __shared__ unsigned int smem[THREADS];
-
const int tid = threadIdx.x;
- const int bin = blockIdx.x;
+ const int group = tid >> 5;
+ const int lane = tid & 31;
+ const int bin = blockIdx.x * REDUCE_BINS + group;
unsigned int sum = 0;
- for (int block = tid; block < BLOCKS; block += THREADS) {
+ for (int block = lane; block < BLOCKS; block += 32) {
sum += partials[block * BINS + bin];
}
- smem[tid] = sum;
- __syncthreads();
-
- #pragma unroll
- for (int offset = THREADS / 2; offset >= 32; offset >>= 1) {
- if (tid < offset) {
- smem[tid] += smem[tid + offset];
- }
- __syncthreads();
+ sum += __shfl_down_sync(0xffffffffu, sum, 16);
+ sum += __shfl_down_sync(0xffffffffu, sum, 8);
+ sum += __shfl_down_sync(0xffffffffu, sum, 4);
+ sum += __shfl_down_sync(0xffffffffu, sum, 2);
+ sum += __shfl_down_sync(0xffffffffu, sum, 1);
+ if (lane == 0) {
+ out[bin] = static_cast<unsigned long long>(sum);
}
-
- if (tid < 32) {
- unsigned int v = smem[tid];
- v += __shfl_down_sync(0xffffffffu, v, 16);
- v += __shfl_down_sync(0xffffffffu, v, 8);
- v += __shfl_down_sync(0xffffffffu, v, 4);
- v += __shfl_down_sync(0xffffffffu, v, 2);
- v += __shfl_down_sync(0xffffffffu, v, 1);
- if (tid == 0) {
- out[bin] = static_cast<unsigned long long>(v);
- }
- }
}
torch::Tensor cuda_histogram_a100(std::vector<torch::Tensor> data) {
⋯ 20 unchanged lines
reinterpret_cast<const uint4*>(data[0].data_ptr<uint8_t>()),
reinterpret_cast<const int*>(hot_bin.data_ptr<int32_t>()),
reinterpret_cast<unsigned int*>(partials.data_ptr<int32_t>()));
- reduce_partials_kernel<<<BINS, THREADS>>>(
+ reduce_partials_kernel<<<BINS / REDUCE_BINS, REDUCE_THREADS>>>(
reinterpret_cast<const unsigned int*>(partials.data_ptr<int32_t>()),
reinterpret_cast<unsigned long long*>(output.data_ptr<int64_t>()));
return output;
⋯ 5 unchanged lines
"""
_EXT = load_inline(
- name="cuda_histogram_a100_extension_010",
+ name="cuda_histogram_a100_extension_017",
cpp_sources=[_CPP_SOURCE],
cuda_sources=[_CUDA_SOURCE],
functions=None,
scrolls · 121 diff lines total

Best evidence level for this revision: reported

JSON