Skip to content
KernelIndex
Search⌘K

submission 885163

Lorenzo · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

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

submission_2.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-cholesky-885163?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
NVIDIA B200
1.59ms
#190 of 337
2026-07-18

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:9f99e8bc4d4eb02ea4f8a61cc548fca7543c20c48f2e61d1257c9af895781708
license declaredunknown
license concludedunknown
authorsLorenzo
imported2026-08-26

Techniques

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

shared-memory__shared__ float col[N];

Kernel source

submission_2.py304 lines
#!POPCORN leaderboard cholesky
#!POPCORN gpu B200
"""Batched dense Cholesky, GPU Mode `cholesky` (B200).

s2 = s1 + a custom BATCHED left-looking Cholesky for medium n (default n==512,
the heaviest shape n=512 b=640). Profiling showed cuSOLVER's batched path uses
block-16 CUDA-core kernels (NO tensor cores) + a full input clone (~1.19ms at
n=512 b640). This kernel:
  * left-looking (reads A panel-by-panel, writes L) → no full clone.
  * FLOP-heavy trailing GEMM  W = A_panel - L_left @ L_top^T  on cuBLAS
    strided-batched TF32 tensor cores.
  * small nb x nb diagonal factor + TRSM in one on-chip FP32 panel kernel.

s1 tiers kept: n==32 warp-reg, n==64 block-reg, n>=1024 & b<=8 per-matrix
non-batched potrf reroute, else cuSOLVER batched.
"""

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

torch.backends.cuda.matmul.allow_tf32 = False
torch.backends.cudnn.allow_tf32 = False

_NB = 32  # panel width for the batched left-looking path

_CUDA_SRC = r"""
#include <torch/extension.h>
#include <ATen/cuda/CUDAContext.h>
#include <cublas_v2.h>
#include <cmath>

// ================= s1 tiny-n kernels =================
template<int N>
__global__ void chol_warp_kernel(const float* __restrict__ A,
                                 float* __restrict__ L, int batch) {
    const int wpb = blockDim.x >> 5;
    const int m = blockIdx.x * wpb + (threadIdx.x >> 5);
    if (m >= batch) return;
    const int lane = threadIdx.x & 31;
    const float* Ab = A + (size_t)m * N * N;
    float* Lb = L + (size_t)m * N * N;
    float a[N];
    #pragma unroll
    for (int c = 0; c < N; ++c) a[c] = (c <= lane) ? Ab[lane * N + c] : 0.0f;
    #pragma unroll
    for (int j = 0; j < N; ++j) {
        float d = sqrtf(__shfl_sync(0xffffffffu, a[j], j));
        float linv = 1.0f / d;
        float lij = (lane > j) ? a[j] * linv : (lane == j ? d : 0.0f);
        if (lane == j) a[j] = d; else if (lane > j) a[j] = lij;
        #pragma unroll
        for (int k = j + 1; k < N; ++k) {
            float lkj = __shfl_sync(0xffffffffu, lij, k);
            if (lane >= k) a[k] -= lij * lkj;
        }
    }
    #pragma unroll
    for (int c = 0; c < N; ++c) Lb[lane * N + c] = (c <= lane) ? a[c] : 0.0f;
}

template<int N>
__global__ void chol_block_kernel(const float* __restrict__ A,
                                   float* __restrict__ L) {
    __shared__ float col[N];
    const int b = blockIdx.x;
    const int i = threadIdx.x;
    const float* Ab = A + (size_t)b * N * N;
    float* Lb = L + (size_t)b * N * N;
    float a[N];
    #pragma unroll
    for (int c = 0; c < N; ++c) a[c] = (c <= i) ? Ab[i * N + c] : 0.0f;
    #pragma unroll
    for (int j = 0; j < N; ++j) {
        if (i == j) col[j] = a[j];
        __syncthreads();
        float d = sqrtf(col[j]);
        float linv = 1.0f / d;
        float lij = (i > j) ? a[j] * linv : (i == j ? d : 0.0f);
        __syncthreads();
        col[i] = lij;
        __syncthreads();
        float ci = col[i];
        #pragma unroll
        for (int k = j + 1; k < N; ++k) if (i >= k) a[k] -= ci * col[k];
        if (i == j) a[j] = d; else if (i > j) a[j] = lij;
        __syncthreads();
    }
    #pragma unroll
    for (int c = 0; c < N; ++c) Lb[i * N + c] = (c <= i) ? a[c] : 0.0f;
}

torch::Tensor chol_warp(torch::Tensor A) {
    const int batch = (int)A.size(0);
    auto L = torch::empty_like(A);
    const int wpb = 8, threads = wpb * 32, blocks = (batch + wpb - 1) / wpb;
    chol_warp_kernel<32><<<blocks, threads>>>(A.data_ptr<float>(), L.data_ptr<float>(), batch);
    return L;
}
torch::Tensor chol_block(torch::Tensor A) {
    const int batch = (int)A.size(0), n = (int)A.size(2);
    auto L = torch::empty_like(A);
    auto pa = A.data_ptr<float>(); auto pl = L.data_ptr<float>();
    if (n == 64) chol_block_kernel<64><<<batch, 64>>>(pa, pl);
    else throw std::runtime_error("chol_block unsupported n");
    return L;
}

// ================= batched left-looking (medium n) =================
static cublasHandle_t cbHandle() {
    static cublasHandle_t h = nullptr;
    if (!h) { if (cublasCreate(&h) != CUBLAS_STATUS_SUCCESS) throw std::runtime_error("cublasCreate"); }
    return h;
}

// Row-major C(m x n) = alpha*opA(A)(m x k) @ opB(B)(k x n) + beta*C, strided-batched.
static void gemm_rm(bool tA, bool tB, int m, int n, int k, float alpha,
                    const float* A, int lda, long long sA,
                    const float* B, int ldb, long long sB, float beta,
                    float* C, int ldc, long long sC, int batch, cublasComputeType_t ct) {
    cublasOperation_t opA = tA ? CUBLAS_OP_T : CUBLAS_OP_N;
    cublasOperation_t opB = tB ? CUBLAS_OP_T : CUBLAS_OP_N;
    cublasStatus_t st = cublasGemmStridedBatchedEx(
        cbHandle(), opB, opA, n, m, k, &alpha,
        B, CUDA_R_32F, ldb, sB, A, CUDA_R_32F, lda, sA,
        &beta, C, CUDA_R_32F, ldc, sC, batch, ct, CUBLAS_GEMM_DEFAULT);
    if (st != CUBLAS_STATUS_SUCCESS) throw std::runtime_error("gemm " + std::to_string((int)st));
}

// Copy A[k:k+R, k:k+cur] -> L[same]  (panel init for the left-looking GEMM).
__global__ void copy_panel_kernel(const float* __restrict__ A, float* __restrict__ L,
                                  int n, int k, int cur, int R) {
    const int b = blockIdx.z;
    const float* Ab = A + (size_t)b * n * n;
    float* Lb = L + (size_t)b * n * n;
    const long long tot = (long long)R * cur;
    for (long long idx = (long long)blockIdx.x * blockDim.x + threadIdx.x;
         idx < tot; idx += (long long)gridDim.x * blockDim.x) {
        int r = idx / cur, c = idx % cur;
        Lb[(size_t)(k + r) * n + (k + c)] = Ab[(size_t)(k + r) * n + (k + c)];
    }
}

// Fast NB=32 panel: warp0 factors the 32x32 diagonal block in registers via
// warp-shuffle (NO __syncthreads chain), then all threads do the TRSM below.
__global__ void panel_factor_warp32(float* __restrict__ L, int n, int k, int R) {
    __shared__ float D[32][33];   // [c][p] = L11[c][p]; +1 pad avoids bank conflicts
    const int b = blockIdx.x;
    float* Lb = L + (size_t)b * n * n;
    const int tid = threadIdx.x, nt = blockDim.x;
    const int warp = tid >> 5, lane = tid & 31;
    if (warp == 0) {
        float a[32];
        #pragma unroll
        for (int c = 0; c < 32; ++c)
            a[c] = (c <= lane) ? Lb[(size_t)(k + lane) * n + (k + c)] : 0.0f;
        #pragma unroll
        for (int j = 0; j < 32; ++j) {
            float d = sqrtf(__shfl_sync(0xffffffffu, a[j], j));
            float linv = 1.0f / d;
            float lij = (lane > j) ? a[j] * linv : (lane == j ? d : 0.0f);
            if (lane == j) a[j] = d; else if (lane > j) a[j] = lij;
            #pragma unroll
            for (int kk = j + 1; kk < 32; ++kk) {
                float lkj = __shfl_sync(0xffffffffu, lij, kk);
                if (lane >= kk) a[kk] -= lij * lkj;
            }
        }
        #pragma unroll
        for (int c = 0; c < 32; ++c) {
            float v = (c <= lane) ? a[c] : 0.0f;
            D[lane][c] = v;                              // D[row][col]
            Lb[(size_t)(k + lane) * n + (k + c)] = v;
        }
    }
    __syncthreads();
    for (int i = 32 + tid; i < R; i += nt) {
        float row[32];
        #pragma unroll
        for (int c = 0; c < 32; ++c) {
            float s = Lb[(size_t)(k + i) * n + (k + c)];
            #pragma unroll
            for (int p = 0; p < c; ++p) s -= row[p] * D[c][p];
            row[c] = s / D[c][c];
            Lb[(size_t)(k + i) * n + (k + c)] = row[c];
        }
    }
}

// Factor the panel L[k:k+R, k:k+cur] (already = updated W) in place:
// diagonal cur x cur Cholesky (FP32) + TRSM of the rows below. One block/matrix.
template<int NB>
__global__ void panel_factor_kernel(float* __restrict__ L, int n, int k, int R) {
    __shared__ float D[NB][NB];
    const int b = blockIdx.x;
    float* Lb = L + (size_t)b * n * n;
    const int tid = threadIdx.x, nt = blockDim.x;
    // load diagonal block (lower); zero strict upper
    for (int idx = tid; idx < NB * NB; idx += nt) {
        int r = idx / NB, c = idx % NB;
        D[r][c] = (r >= c) ? Lb[(size_t)(k + r) * n + (k + c)] : 0.0f;
    }
    __syncthreads();
    // left-looking factor of the diagonal block
    for (int j = 0; j < NB; ++j) {
        if (tid == 0) {
            float s = D[j][j];
            for (int p = 0; p < j; ++p) s -= D[j][p] * D[j][p];
            D[j][j] = sqrtf(s);
        }
        __syncthreads();
        float djj = D[j][j];
        for (int i = j + 1 + tid; i < NB; i += nt) {
            float s = D[i][j];
            for (int p = 0; p < j; ++p) s -= D[i][p] * D[j][p];
            D[i][j] = s / djj;
        }
        __syncthreads();
    }
    // write diagonal block back (lower kept, strict upper zeroed)
    for (int idx = tid; idx < NB * NB; idx += nt) {
        int r = idx / NB, c = idx % NB;
        Lb[(size_t)(k + r) * n + (k + c)] = (r >= c) ? D[r][c] : 0.0f;
    }
    // TRSM: rows i in [NB, R): L21[i,c] = (W[i,c] - sum_{p<c} L21[i,p]*D[c,p]) / D[c,c]
    for (int i = NB + tid; i < R; i += nt) {
        float row[NB];
        #pragma unroll
        for (int c = 0; c < NB; ++c) {
            float s = Lb[(size_t)(k + i) * n + (k + c)];
            #pragma unroll
            for (int p = 0; p < c; ++p) s -= row[p] * D[c][p];
            row[c] = s / D[c][c];
            Lb[(size_t)(k + i) * n + (k + c)] = row[c];
        }
    }
}

torch::Tensor chol_batched(torch::Tensor A, int64_t nb) {
    const int batch = (int)A.size(0), n = (int)A.size(2);
    if (n % nb != 0) throw std::runtime_error("n must be divisible by nb");
    auto L = torch::zeros_like(A);           // strict upper stays 0
    const float* pa = A.data_ptr<float>();
    float* pl = L.data_ptr<float>();
    const long long S = (long long)n * n;
    const cublasComputeType_t ct = CUBLAS_COMPUTE_32F_FAST_TF32;

    for (int k = 0; k < n; k += (int)nb) {
        int cur = (int)nb; int R = n - k;
        // copy A panel -> L panel
        {
            long long tot = (long long)R * cur;
            int th = 256; int bl = (int)((tot + th - 1) / th); if (bl > 512) bl = 512;
            dim3 grid(bl, 1, batch);
            copy_panel_kernel<<<grid, th>>>(pa, pl, n, k, cur, R);
        }
        // trailing: L[k:n,k:k+cur] = -L[k:n,0:k] @ L[k:k+cur,0:k]^T + A_panel
        if (k > 0) {
            const float* Aleft = pl + (size_t)k * n + 0;   // L[k, 0]
            float* C = pl + (size_t)k * n + k;             // L[k, k]
            gemm_rm(false, true, R, cur, k, -1.0f,
                    Aleft, n, S, Aleft, n, S, 1.0f, C, n, S, batch, ct);
        }
        // panel factor (diag + trsm)
        if (nb == 32) panel_factor_warp32<<<batch, 256>>>(pl, n, k, R);
        else if (nb == 64) panel_factor_kernel<64><<<batch, 256>>>(pl, n, k, R);
        else throw std::runtime_error("unsupported nb");
    }
    cudaError_t e = cudaGetLastError();
    if (e != cudaSuccess) throw std::runtime_error(std::string("chol_batched: ") + cudaGetErrorString(e));
    return L;
}
"""

_CPP_SRC = ("torch::Tensor chol_warp(torch::Tensor A);\n"
            "torch::Tensor chol_block(torch::Tensor A);\n"
            "torch::Tensor chol_batched(torch::Tensor A, int64_t nb);")

_MOD = load_inline(
    name="chol_ext_s2",
    cpp_sources=_CPP_SRC,
    cuda_sources=_CUDA_SRC,
    functions=["chol_warp", "chol_block", "chol_batched"],
    extra_cuda_cflags=["-O3", "--use_fast_math"],
    extra_ldflags=["-lcublas"],
    verbose=False,
)


def custom_kernel(data: input_t) -> output_t:
    A = data.contiguous()
    n = A.size(-1)
    if n == 32:
        return _MOD.chol_warp(A)
    if n == 64:
        return _MOD.chol_block(A)
    if n == 512:
        return _MOD.chol_batched(A, _NB)
    if n >= 1024 and A.size(0) <= 8:
        outs = [torch.linalg.cholesky_ex(A[i], check_errors=False).L
                for i in range(A.size(0))]
        return torch.stack(outs, 0)
    return torch.linalg.cholesky_ex(A, check_errors=False).L
scrolls · 304 lines total

Source code from GPU Mode and the KernelBot dataset · June 9 Researcher Reciprocity License v1.0

Best evidence level for this revision: reported

JSON