submission 506537
ağaç.mp4 · python · License unknown
Use it
Vendorable · source mirrored · license unknownView source →
No package. Vendor the mirrored source: 124 lines, June 9 Researcher Reciprocity License v1.0.
submission.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-histogram-v2-506537?include=source"interfacepython
Compatibility
measured onNVIDIA B200
declared hardwareNVIDIA B200
architecturessm_100
dtypesuint8
Benchmark evidence
1 measurement across 1 GPU, fastest first.
Reported · How evidence levels are derived →
Source and license
sourceavailable
revision digestsha256:0047f23742049aafbf4c4faa9e00b9d60cc95778b281c8bf94aae3e921f01b79
license declaredunknown
license concludedunknown
authorsağaç.mp4
imported2026-08-15
Techniques
Extracted from the mirrored source by pattern, never inferred. Each row cites its line.
shared-memory
__shared__ unsigned int smem[NUM_BINS];vector-width = uint4
const uint4* __restrict__ data16,Kernel source
submission.py124 lines
"""
Histogram v2 — Direct global atomicAdd, block0 zeros output at start
@MemoryCoalesced
Dynamic SM count: num_blocks = sm_count * 2, works optimally on any GPU.
L4: 58 SMs → 116 blocks, B200: 132 SMs → 264 blocks.
"""
import torch
from torch.utils.cpp_extension import load_inline
cpp_source = """
void histogram_cuda(
torch::Tensor data,
torch::Tensor output,
torch::Tensor flag);
"""
cuda_source = r"""
#include <torch/extension.h>
#include <cuda_runtime.h>
#include <stdint.h>
#define BLOCK_THREADS 256
#define NUM_BINS 256
__global__ void __launch_bounds__(BLOCK_THREADS, 4)
histogram_kernel(
const uint4* __restrict__ data16,
unsigned long long* __restrict__ output,
volatile int* __restrict__ flag,
int n16,
int n,
int num_blocks)
{
if (blockIdx.x == 0) {
output[threadIdx.x] = 0ULL;
__threadfence();
if (threadIdx.x == 0)
*flag = 1;
} else {
if (threadIdx.x == 0)
while (*flag == 0) {}
__syncthreads();
}
__shared__ unsigned int smem[NUM_BINS];
smem[threadIdx.x] = 0u;
__syncthreads();
const int tid = blockIdx.x * BLOCK_THREADS + threadIdx.x;
const int stride = num_blocks * BLOCK_THREADS;
for (int i = tid; i < n16; i += stride) {
uint4 v = __ldg(data16 + i);
atomicAdd(&smem[(v.x ) & 0xff], 1u);
atomicAdd(&smem[(v.x >> 8) & 0xff], 1u);
atomicAdd(&smem[(v.x >> 16) & 0xff], 1u);
atomicAdd(&smem[(v.x >> 24) & 0xff], 1u);
atomicAdd(&smem[(v.y ) & 0xff], 1u);
atomicAdd(&smem[(v.y >> 8) & 0xff], 1u);
atomicAdd(&smem[(v.y >> 16) & 0xff], 1u);
atomicAdd(&smem[(v.y >> 24) & 0xff], 1u);
atomicAdd(&smem[(v.z ) & 0xff], 1u);
atomicAdd(&smem[(v.z >> 8) & 0xff], 1u);
atomicAdd(&smem[(v.z >> 16) & 0xff], 1u);
atomicAdd(&smem[(v.z >> 24) & 0xff], 1u);
atomicAdd(&smem[(v.w ) & 0xff], 1u);
atomicAdd(&smem[(v.w >> 8) & 0xff], 1u);
atomicAdd(&smem[(v.w >> 16) & 0xff], 1u);
atomicAdd(&smem[(v.w >> 24) & 0xff], 1u);
}
const uint8_t* data1 = (const uint8_t*)data16;
for (int i = n16 * 16 + tid; i < n; i += stride)
atomicAdd(&smem[__ldg(data1 + i)], 1u);
__syncthreads();
atomicAdd(&output[threadIdx.x], (unsigned long long)smem[threadIdx.x]);
if (blockIdx.x == 0 && threadIdx.x == 0)
*flag = 0;
}
static int NUM_BLOCKS_CACHED = 0;
void histogram_cuda(
torch::Tensor data,
torch::Tensor output,
torch::Tensor flag)
{
if (NUM_BLOCKS_CACHED == 0) {
int dev, sm_count;
cudaGetDevice(&dev);
cudaDeviceGetAttribute(&sm_count, cudaDevAttrMultiProcessorCount, dev);
NUM_BLOCKS_CACHED = sm_count * 2;
}
const int n = data.numel();
const int n16 = n / 16;
histogram_kernel<<<NUM_BLOCKS_CACHED, BLOCK_THREADS>>>(
(const uint4*)data.data_ptr<uint8_t>(),
(unsigned long long*)output.data_ptr<int64_t>(),
(volatile int*)flag.data_ptr<int>(),
n16, n, NUM_BLOCKS_CACHED
);
}
"""
module = load_inline(
name='histogram_v21',
cpp_sources=cpp_source,
cuda_sources=cuda_source,
functions=['histogram_cuda'],
verbose=False,
extra_cuda_cflags=['-O3', '--use_fast_math', '-std=c++17'],
)
_flag = torch.zeros(1, device='cuda', dtype=torch.int32)
def custom_kernel(data: tuple) -> torch.Tensor:
inp, out = data
module.histogram_cuda(inp, out, _flag)
return out
scrolls · 124 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 506501.
⋯ 1 unchanged linesHistogram v2 — Direct global atomicAdd, block0 zeros output at start@MemoryCoalesced- v18 bottleneck: scratch merge writes+reads 131KB extra per call.- For 1.3MB input that's 10% overhead → fixed 1.5µs cost.-- Fix: eliminate scratch entirely.- - Block 0 zeros output[0..255] immediately (256 stores, ~0.5µs)- - Block 0 sets ready flag- - All other blocks spin on ready flag at START (near-zero wait)- - All blocks accumulate into smem- - All blocks atomicAdd smem → output (2KB, 256 bins × num_blocks atomics)-- Spin at START vs END:- - END spin: wait for slowest block to finish processing its data chunk → 10-15µs- - START spin: wait for block 0 to do 256 stores → ~0.1µs- ∴ START spin is ~100x cheaper-- Global atomicAdd contention at merge:- - 128 blocks × 256 bins = 32K atomics, but spread across 256 independent addresses- - 128 serialized atomics per bin × ~5ns = ~640ns = 0.6µs- - Still cheaper than 131KB scratch write+read (~1.5µs)+ Dynamic SM count: num_blocks = sm_count * 2, works optimally on any GPU.+ L4: 58 SMs → 116 blocks, B200: 132 SMs → 264 blocks."""import torch⋯ 13 unchanged lines#define BLOCK_THREADS 256#define NUM_BINS 256- #define NUM_BLOCKS 116__global__ void __launch_bounds__(BLOCK_THREADS, 4)histogram_kernel(const uint4* __restrict__ data16,unsigned long long* __restrict__ output,- volatile int* __restrict__ flag, // [1], reset to 0 by block 0 at end+ volatile int* __restrict__ flag,int n16,- int n)+ int n,+ int num_blocks){- // Block 0: zero output immediately, then signal ready- // All others: spin until ready, then start workif (blockIdx.x == 0) {output[threadIdx.x] = 0ULL;__threadfence();⋯ 10 unchanged lines__syncthreads();const int tid = blockIdx.x * BLOCK_THREADS + threadIdx.x;- const int stride = NUM_BLOCKS * BLOCK_THREADS;+ const int stride = num_blocks * BLOCK_THREADS;for (int i = tid; i < n16; i += stride) {uint4 v = __ldg(data16 + i);⋯ 20 unchanged lines__syncthreads();- // Merge smem → global (output already zeroed)atomicAdd(&output[threadIdx.x], (unsigned long long)smem[threadIdx.x]);- // Block 0 resets flag for next call (it's guaranteed last to finish- // since it did the most work: zeroing + full data processing)if (blockIdx.x == 0 && threadIdx.x == 0)*flag = 0;}+ static int NUM_BLOCKS_CACHED = 0;+void histogram_cuda(torch::Tensor data,torch::Tensor output,torch::Tensor flag){+ if (NUM_BLOCKS_CACHED == 0) {+ int dev, sm_count;+ cudaGetDevice(&dev);+ cudaDeviceGetAttribute(&sm_count, cudaDevAttrMultiProcessorCount, dev);+ NUM_BLOCKS_CACHED = sm_count * 2;+ }const int n = data.numel();const int n16 = n / 16;- histogram_kernel<<<NUM_BLOCKS, BLOCK_THREADS>>>(+ histogram_kernel<<<NUM_BLOCKS_CACHED, BLOCK_THREADS>>>((const uint4*)data.data_ptr<uint8_t>(),(unsigned long long*)output.data_ptr<int64_t>(),(volatile int*)flag.data_ptr<int>(),- n16, n+ n16, n, NUM_BLOCKS_CACHED);}"""module = load_inline(- name='histogram_v19',+ name='histogram_v21',cpp_sources=cpp_source,cuda_sources=cuda_source,functions=['histogram_cuda'],⋯ 1 unchanged linesextra_cuda_cflags=['-O3', '--use_fast_math', '-std=c++17'],)- # Single int flag, reset to 0 by kernel after each call_flag = torch.zeros(1, device='cuda', dtype=torch.int32)def custom_kernel(data: tuple) -> torch.Tensor:
scrolls · 112 diff lines total
Best evidence level for this revision: reported
JSON