Skip to content
KernelIndex
Search⌘K

submission 68006

ethylene · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

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

submission.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-grayscale-v2-68006?include=source"
interfacepython
Compatibility
measured onNVIDIA B200
declared hardwareNVIDIA B200
architecturessm_100
dtypesfp32

Benchmark evidence

1 measurement across 1 GPU, fastest first.

Operation / workload
Hardware
Latency
Rank
Observed
RGB to grayscalesuite of 6 cases
NVIDIA B200
656.2µs
#62 of 84
2025-11-08

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:46556708a35470b189ef74d537bd3782644401934882f90381be7f3f13864a49
license declaredunknown
license concludedunknown
authorsethylene
imported2026-08-15

Kernel source

submission.py171 lines
from utils import make_match_reference, DeterministicContext
import torch
from task import input_t, output_t


def ref_kernel(data: input_t) -> output_t:
    """
    Reference implementation of RGB to grayscale conversion using PyTorch.
    Uses the standard coefficients: Y = 0.2989 R + 0.5870 G + 0.1140 B

    Args:
        data: RGB tensor of shape (H, W, 3) with values in [0, 1]
    Returns:
        Grayscale tensor of shape (H, W) with values in [0, 1]
    """
    with DeterministicContext():
        data, output = data
        # Standard RGB to Grayscale coefficients
        weights = torch.tensor(
            [0.2989, 0.5870, 0.1140], device=data.device, dtype=data.dtype
        )
        output[...] = torch.sum(data * weights, dim=-1)
        return output


def generate_input(size: int, seed: int) -> input_t:
    """
    Generates random RGB image tensor of specified size.
    Returns:
        Tensor of shape (size, size, 3) with values in [0, 1]
    """
    gen = torch.Generator(device="cuda")
    gen.manual_seed(seed)

    x = torch.rand(
        size, size, 3, device="cuda", dtype=torch.float32, generator=gen
    ).contiguous()

    y = torch.empty(size, size, device="cuda", dtype=torch.float32).contiguous()

    return x, y


check_implementation = make_match_reference(ref_kernel, rtol=1e-4, atol=1e-4)

import torch
from torch.utils.cpp_extension import load_inline
from task import input_t, output_t

# Choose arch flags to generate native SASS for your GPU (e.g., B200 -> sm_100).
_cc = "".join(map(str, torch.cuda.get_device_capability()))
_cuda_cflags = [
    "-O3",
    "--use_fast_math",
    f"-gencode=arch=compute_{_cc},code=sm_{_cc}",
    f"-gencode=arch=compute_{_cc},code=compute_{_cc}",
]

rgb2gray_cpp_source = r"""
#include <torch/extension.h>
torch::Tensor rgb2gray_cuda(torch::Tensor x, torch::Tensor out);
"""

# Build once; if arch flag isn't recognized, retry without it.
rgb2gray_module = load_inline(
    name="rgb2gray_cuda",
    cpp_sources=rgb2gray_cpp_source,
    cuda_sources="""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cuda_fp16.h>
#include <cstdint>

template <typename scalar_t>
__global__ void rgb2gray_kernel_fp(const scalar_t* __restrict__ x,
                                   scalar_t* __restrict__ y,
                                   int64_t N) {
    const int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;
    const int64_t stride = (int64_t)blockDim.x * gridDim.x;
    for (int64_t i = idx; i < N; i += stride) {
        const int64_t j = i * 3; // HWC contiguous: 3 scalars per pixel
        scalar_t r = x[j + 0];
        scalar_t g = x[j + 1];
        scalar_t b = x[j + 2];
        y[i] = r * scalar_t(0.2989) + g * scalar_t(0.5870) + b * scalar_t(0.1140);
    }
}

__global__ void rgb2gray_kernel_half(const __half* __restrict__ x,
                                     __half* __restrict__ y,
                                     int64_t N) {
    const int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;
    const int64_t stride = (int64_t)blockDim.x * gridDim.x;
    for (int64_t i = idx; i < N; i += stride) {
        const int64_t j = i * 3;
        float r = __half2float(x[j + 0]);
        float g = __half2float(x[j + 1]);
        float b = __half2float(x[j + 2]);
        float val = __fmaf_rn(r, 0.2989f, __fmaf_rn(g, 0.5870f, b * 0.1140f));
        y[i] = __float2half(val);
    }
}

torch::Tensor rgb2gray_cuda(torch::Tensor x, torch::Tensor out) {
    TORCH_CHECK(x.device().is_cuda(), "x must be CUDA");
    TORCH_CHECK(out.device().is_cuda(), "out must be CUDA");
    TORCH_CHECK(x.is_contiguous() && out.is_contiguous(), "x/out must be contiguous");
    TORCH_CHECK(x.dim() == 3 && x.size(2) == 3, "x must be HxWx3");
    TORCH_CHECK(out.dim() == 2 && out.size(0) == x.size(0) && out.size(1) == x.size(1),
                "out must be HxW matching x's H,W");
    TORCH_CHECK(x.scalar_type() == out.scalar_type(), "dtype mismatch x/out");

    const int64_t N = x.size(0) * x.size(1);
    if (N == 0) return out;

    const int threads = 1024;  // good bandwidth setting on B200
    const int sm = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;
    const int max_blocks = sm * 16;
    const int blocks = (int)std::min<int64_t>((N + threads - 1) / threads, (int64_t)max_blocks);
    auto stream = at::cuda::getCurrentCUDAStream();

    if (x.scalar_type() == at::kHalf) {
        const __half* xp = reinterpret_cast<const __half*>(x.data_ptr<at::Half>());
        __half* yp = reinterpret_cast<__half*>(out.data_ptr<at::Half>());
        rgb2gray_kernel_half<<<blocks, threads, 0, stream>>>(xp, yp, N);
    } else {
        AT_DISPATCH_FLOATING_TYPES(x.scalar_type(), "rgb2gray_kernel_fp", ([&] {
            const scalar_t* xp = x.data_ptr<scalar_t>();
            scalar_t* yp = out.data_ptr<scalar_t>();
            rgb2gray_kernel_fp<scalar_t><<<blocks, threads, 0, stream>>>(xp, yp, N);
        }));
    }

    cudaError_t err = cudaGetLastError();
    if (err != cudaSuccess) {
        throw std::runtime_error(cudaGetErrorString(err));
    }
    return out;
}
""",
    extra_cuda_cflags=_cuda_cflags,
    extra_cflags=["-O3"],
    functions=["rgb2gray_cuda"],
    verbose=False,
)


def rgb2gray(A: torch.Tensor, Out: torch.Tensor) -> torch.Tensor:
    return rgb2gray_module.rgb2gray_cuda(A, Out)


def custom_kernel(data: input_t) -> output_t:
    """
    Fast CUDA RGB->grayscale: single pass, no temporaries.
    Works with float16/float32/float64. Expects HxWx3 (contiguous) -> HxW (contiguous).
    """
    x, out = data
    if not x.is_cuda or not out.is_cuda:
        raise RuntimeError("Both tensors must be on GPU")
    if x.dim() != 3 or x.size(-1) != 3:
        raise RuntimeError("Input must be HxWx3")
    if not x.is_contiguous() or not out.is_contiguous():
        x = x.contiguous()
        out = out.contiguous()
    return rgb2gray(x, out)


x, y = generate_input(1024, seed=42)
results = check_implementation((x, y), custom_kernel((x, y)))
print("Check implementation result:", results)
scrolls · 171 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 67968.

⋯ 42 unchanged lines
check_implementation = make_match_reference(ref_kernel, rtol=1e-4, atol=1e-4)
+ import torch
+ from torch.utils.cpp_extension import load_inline
+ from task import input_t, output_t
+ # Choose arch flags to generate native SASS for your GPU (e.g., B200 -> sm_100).
+ _cc = "".join(map(str, torch.cuda.get_device_capability()))
+ _cuda_cflags = [
+ "-O3",
+ "--use_fast_math",
+ f"-gencode=arch=compute_{_cc},code=sm_{_cc}",
+ f"-gencode=arch=compute_{_cc},code=compute_{_cc}",
+ ]
+
+ rgb2gray_cpp_source = r"""
+ #include <torch/extension.h>
+ torch::Tensor rgb2gray_cuda(torch::Tensor x, torch::Tensor out);
+ """
+
+ # Build once; if arch flag isn't recognized, retry without it.
+ rgb2gray_module = load_inline(
+ name="rgb2gray_cuda",
+ cpp_sources=rgb2gray_cpp_source,
+ cuda_sources="""
+ #include <torch/extension.h>
+ #include <ATen/cuda/CUDAContext.h>
+ #include <cuda_fp16.h>
+ #include <cstdint>
+
+ template <typename scalar_t>
+ __global__ void rgb2gray_kernel_fp(const scalar_t* __restrict__ x,
+ scalar_t* __restrict__ y,
+ int64_t N) {
+ const int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;
+ const int64_t stride = (int64_t)blockDim.x * gridDim.x;
+ for (int64_t i = idx; i < N; i += stride) {
+ const int64_t j = i * 3; // HWC contiguous: 3 scalars per pixel
+ scalar_t r = x[j + 0];
+ scalar_t g = x[j + 1];
+ scalar_t b = x[j + 2];
+ y[i] = r * scalar_t(0.2989) + g * scalar_t(0.5870) + b * scalar_t(0.1140);
+ }
+ }
+
+ __global__ void rgb2gray_kernel_half(const __half* __restrict__ x,
+ __half* __restrict__ y,
+ int64_t N) {
+ const int64_t idx = blockIdx.x * blockDim.x + threadIdx.x;
+ const int64_t stride = (int64_t)blockDim.x * gridDim.x;
+ for (int64_t i = idx; i < N; i += stride) {
+ const int64_t j = i * 3;
+ float r = __half2float(x[j + 0]);
+ float g = __half2float(x[j + 1]);
+ float b = __half2float(x[j + 2]);
+ float val = __fmaf_rn(r, 0.2989f, __fmaf_rn(g, 0.5870f, b * 0.1140f));
+ y[i] = __float2half(val);
+ }
+ }
+
+ torch::Tensor rgb2gray_cuda(torch::Tensor x, torch::Tensor out) {
+ TORCH_CHECK(x.device().is_cuda(), "x must be CUDA");
+ TORCH_CHECK(out.device().is_cuda(), "out must be CUDA");
+ TORCH_CHECK(x.is_contiguous() && out.is_contiguous(), "x/out must be contiguous");
+ TORCH_CHECK(x.dim() == 3 && x.size(2) == 3, "x must be HxWx3");
+ TORCH_CHECK(out.dim() == 2 && out.size(0) == x.size(0) && out.size(1) == x.size(1),
+ "out must be HxW matching x's H,W");
+ TORCH_CHECK(x.scalar_type() == out.scalar_type(), "dtype mismatch x/out");
+
+ const int64_t N = x.size(0) * x.size(1);
+ if (N == 0) return out;
+
+ const int threads = 1024; // good bandwidth setting on B200
+ const int sm = at::cuda::getCurrentDeviceProperties()->multiProcessorCount;
+ const int max_blocks = sm * 16;
+ const int blocks = (int)std::min<int64_t>((N + threads - 1) / threads, (int64_t)max_blocks);
+ auto stream = at::cuda::getCurrentCUDAStream();
+
+ if (x.scalar_type() == at::kHalf) {
+ const __half* xp = reinterpret_cast<const __half*>(x.data_ptr<at::Half>());
+ __half* yp = reinterpret_cast<__half*>(out.data_ptr<at::Half>());
+ rgb2gray_kernel_half<<<blocks, threads, 0, stream>>>(xp, yp, N);
+ } else {
+ AT_DISPATCH_FLOATING_TYPES(x.scalar_type(), "rgb2gray_kernel_fp", ([&] {
+ const scalar_t* xp = x.data_ptr<scalar_t>();
+ scalar_t* yp = out.data_ptr<scalar_t>();
+ rgb2gray_kernel_fp<scalar_t><<<blocks, threads, 0, stream>>>(xp, yp, N);
+ }));
+ }
+
+ cudaError_t err = cudaGetLastError();
+ if (err != cudaSuccess) {
+ throw std::runtime_error(cudaGetErrorString(err));
+ }
+ return out;
+ }
+ """,
+ extra_cuda_cflags=_cuda_cflags,
+ extra_cflags=["-O3"],
+ functions=["rgb2gray_cuda"],
+ verbose=False,
+ )
+
+
+ def rgb2gray(A: torch.Tensor, Out: torch.Tensor) -> torch.Tensor:
+ return rgb2gray_module.rgb2gray_cuda(A, Out)
+
+
def custom_kernel(data: input_t) -> output_t:
- # TODO make this faster
- data, output = data
- # Standard RGB to Grayscale coefficients
- weights = torch.tensor(
- [0.2989, 0.5870, 0.1140], device=data.device, dtype=data.dtype
- )
- output[...] = torch.sum(data * weights, dim=-1)
- return output
+ """
+ Fast CUDA RGB->grayscale: single pass, no temporaries.
+ Works with float16/float32/float64. Expects HxWx3 (contiguous) -> HxW (contiguous).
+ """
+ x, out = data
+ if not x.is_cuda or not out.is_cuda:
+ raise RuntimeError("Both tensors must be on GPU")
+ if x.dim() != 3 or x.size(-1) != 3:
+ raise RuntimeError("Input must be HxWx3")
+ if not x.is_contiguous() or not out.is_contiguous():
+ x = x.contiguous()
+ out = out.contiguous()
+ return rgb2gray(x, out)
+
+
+ x, y = generate_input(1024, seed=42)
+ results = check_implementation((x, y), custom_kernel((x, y)))
+ print("Check implementation result:", results)
scrolls · 137 diff lines total

Best evidence level for this revision: reported

JSON