Skip to content
KernelIndex
Search⌘K

submission 866330

matt0279897 · python · License unknown

Use it

Vendorable · source mirrored · license unknownView source →

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

submission.py
curl "https://kernelindex.com/api/v1/implementations/kernelbot-eigh-866330?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
48.7ms
#146 of 286
2026-07-10

Reported · How evidence levels are derived →

Source and license

sourceavailable
revision digestsha256:8f2890070cf899ccb21055a972bf82dce09546ac230ce2ac5b3d192f592bf195
license declaredunknown
license concludedunknown
authorsmatt0279897
imported2026-08-26

Techniques

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

shared-memoryextern __shared__ char sm[];

Kernel source

submission.py182 lines
"""Batched real symmetric eigendecomposition for B200 (popcorn `eigh` task).

Two-sided **block Jacobi** eigensolver as a CUDA kernel, one block per matrix.
A and V live in global memory; only the current 2b x 2b block-pair tile and its
rotation R live in shared memory (<=48KB, which avoids a B200 >48KB dynamic-
shared correctness bug). Each block-pair (I,J): load the 2b x 2b submatrix,
diagonalize it with inner element-Jacobi -> R, then apply R to block-columns
I,J and R^T to block-rows I,J of the whole matrix (and to V's columns). This
cuts global traffic to ~O(sweeps * n^3 / b) vs element Jacobi's O(sweeps*n^3),
and the parallel round-robin gives O(n)-depth sweeps (vs QL's O(n^2)).

Block Jacobi is robust to clustered/repeated/rank-deficient spectra (it rotates
within eigenspaces). Returns (Q, L): Q (b,n,n) orthonormal eigenvector columns,
L (b,n) ascending eigenvalues.
"""
import os

import torch
from torch.utils.cpp_extension import load_inline

from task import input_t, output_t

_FALLBACK = os.environ.get("EIGH_FALLBACK", "0") == "1"
BJ_B = 16          # block-column width (2b=32 tile); n must be divisible by BJ_B
BJ_SWEEPS = 8
BJ_INNER = 6
BJ_MAX_N = 0       # DISABLED: block Jacobi is ~60x slower than cuSOLVER (see notes);
                   # keep code for optimization, but use torch baseline for a passing entry

_CPP_SRC = r"""
#include <torch/extension.h>
#include <vector>
std::vector<torch::Tensor> bjac(torch::Tensor A, int b, int sweeps, int inner);
"""

_CUDA_SRC = r"""
#include <torch/extension.h>
#include <cuda_runtime.h>
#include <math.h>
#define MAXBB 64

__device__ void inner_jacobi(float* M, float* R, int m, int* arr, float* sc, float* ss,
                             int* spp, int* sqq, int sweeps, int tid, int nt) {
    int half = m/2;
    for (int idx=tid; idx<m*m; idx+=nt){int r=idx/m,c=idx%m; R[idx]=(r==c)?1.0f:0.0f;}
    __syncthreads();
    for (int sw=0; sw<sweeps; ++sw) {
        for (int i=tid;i<m;i+=nt) arr[i]=i;
        __syncthreads();
        for (int rnd=0; rnd<m-1; ++rnd) {
            for (int pi=tid; pi<half; pi+=nt) {
                int p=arr[pi], qq=arr[m-1-pi]; if(p>qq){int t=p;p=qq;qq=t;}
                spp[pi]=p; sqq[pi]=qq;
                float app=M[p*m+p],aqq=M[qq*m+qq],apq=M[p*m+qq];
                float c=1.f,s=0.f;
                if(fabsf(apq)>0.f){float tau=(aqq-app)/(2.f*apq);
                    float t=copysignf(1.f,tau)/(fabsf(tau)+sqrtf(1.f+tau*tau)); c=rsqrtf(1.f+t*t); s=t*c;}
                sc[pi]=c; ss[pi]=s;
            }
            __syncthreads();
            for(int idx=tid;idx<half*m;idx+=nt){int pi=idx/m,k=idx%m;int p=spp[pi],qq=sqq[pi];float c=sc[pi],s=ss[pi];
                float a=M[k*m+p],b2=M[k*m+qq];M[k*m+p]=c*a-s*b2;M[k*m+qq]=s*a+c*b2;}
            __syncthreads();
            for(int idx=tid;idx<half*m;idx+=nt){int pi=idx/m,k=idx%m;int p=spp[pi],qq=sqq[pi];float c=sc[pi],s=ss[pi];
                float a=M[p*m+k],b2=M[qq*m+k];M[p*m+k]=c*a-s*b2;M[qq*m+k]=s*a+c*b2;}
            __syncthreads();
            for(int idx=tid;idx<half*m;idx+=nt){int pi=idx/m,k=idx%m;int p=spp[pi],qq=sqq[pi];float c=sc[pi],s=ss[pi];
                float a=R[k*m+p],b2=R[k*m+qq];R[k*m+p]=c*a-s*b2;R[k*m+qq]=s*a+c*b2;}
            __syncthreads();
            if(tid==0){int last=arr[m-1];for(int i=m-1;i>=2;--i)arr[i]=arr[i-1];arr[1]=last;}
            __syncthreads();
        }
    }
}

__global__ void bjac_kernel(float* Ag, float* Vg, float* Lg, int B, int n, int b, int sweeps, int inner) {
    int bid=blockIdx.x; if(bid>=B) return;
    int tid=threadIdx.x, nt=blockDim.x;
    int q=n/b, bb=2*b;
    float* A=Ag+(size_t)bid*n*n; float* V=Vg+(size_t)bid*n*n;
    extern __shared__ char sm[];
    float* sM=(float*)sm; float* sR=sM+bb*bb;
    int* iarr=(int*)(sR+bb*bb); float* isc=(float*)(iarr+bb); float* iss=isc+b;
    int* ispp=(int*)(iss+b); int* isqq=ispp+b; int* blk=isqq+b;
    for(size_t idx=tid;idx<(size_t)n*n;idx+=nt) V[idx]=0.0f;
    for(int i=tid;i<n;i+=nt) V[(size_t)i*n+i]=1.0f;
    __syncthreads();
    int qe = q + (q & 1);       // pad to even with phantom block-column (index >= q)
    for(int sw=0; sw<sweeps; ++sw){
        for(int i=tid;i<qe;i+=nt) blk[i]=i;
        __syncthreads();
        for(int rnd=0; rnd<qe-1; ++rnd){
            for(int bp=0; bp<qe/2; ++bp){
                int I=blk[bp], J=blk[qe-1-bp]; if(I>J){int t=I;I=J;J=t;}
                if(J>=q){ __syncthreads(); continue; }
                int Ic=I*b, Jc=J*b;
                for(int idx=tid;idx<bb*bb;idx+=nt){int r=idx/bb,c=idx%bb;
                    int gr=(r<b)?Ic+r:Jc+(r-b); int gc=(c<b)?Ic+c:Jc+(c-b);
                    sM[idx]=A[(size_t)gr*n+gc];}
                __syncthreads();
                inner_jacobi(sM,sR,bb,iarr,isc,iss,ispp,isqq,inner,tid,nt);
                __syncthreads();
                // apply R to columns I,J of A
                for(int k=tid;k<n;k+=nt){
                    float x[MAXBB];
                    for(int c=0;c<bb;++c){int gc=(c<b)?Ic+c:Jc+(c-b); x[c]=A[(size_t)k*n+gc];}
                    for(int o=0;o<bb;++o){float acc=0.f; for(int c=0;c<bb;++c) acc+=x[c]*sR[c*bb+o];
                        int gc=(o<b)?Ic+o:Jc+(o-b); A[(size_t)k*n+gc]=acc;}
                }
                __syncthreads();
                // apply R^T to rows I,J of A
                for(int k=tid;k<n;k+=nt){
                    float y[MAXBB];
                    for(int r=0;r<bb;++r){int gr=(r<b)?Ic+r:Jc+(r-b); y[r]=A[(size_t)gr*n+k];}
                    for(int o=0;o<bb;++o){float acc=0.f; for(int r=0;r<bb;++r) acc+=sR[r*bb+o]*y[r];
                        int gr=(o<b)?Ic+o:Jc+(o-b); A[(size_t)gr*n+k]=acc;}
                }
                __syncthreads();
                // apply R to columns I,J of V
                for(int k=tid;k<n;k+=nt){
                    float x[MAXBB];
                    for(int c=0;c<bb;++c){int gc=(c<b)?Ic+c:Jc+(c-b); x[c]=V[(size_t)k*n+gc];}
                    for(int o=0;o<bb;++o){float acc=0.f; for(int c=0;c<bb;++c) acc+=x[c]*sR[c*bb+o];
                        int gc=(o<b)?Ic+o:Jc+(o-b); V[(size_t)k*n+gc]=acc;}
                }
                __syncthreads();
            }
            if(tid==0){int last=blk[qe-1];for(int i=qe-1;i>=2;--i)blk[i]=blk[i-1];blk[1]=last;}
            __syncthreads();
        }
    }
    for(int i=tid;i<n;i+=nt) Lg[(size_t)bid*n+i]=A[(size_t)i*n+i];
}

std::vector<torch::Tensor> bjac(torch::Tensor A, int b, int sweeps, int inner){
    TORCH_CHECK(A.is_cuda() && A.dtype()==torch::kFloat32);
    int B=A.size(0), n=A.size(-1);
    auto Aw=A.contiguous().clone(); auto V=torch::empty({B,n,n},A.options()); auto L=torch::empty({B,n},A.options());
    int q=n/b, bb=2*b, nt=256;
    size_t sh=((size_t)2*bb*bb + bb + 4*b + q + 2)*sizeof(float) + 64;
    bjac_kernel<<<B,nt,sh>>>(Aw.data_ptr<float>(),V.data_ptr<float>(),L.data_ptr<float>(),B,n,b,sweeps,inner);
    cudaError_t e=cudaGetLastError(); TORCH_CHECK(e==cudaSuccess,"bjac launch: ",cudaGetErrorString(e)," n=",n);
    return {L,V};
}
"""

_ext = None


def _get_ext():
    global _ext
    if _ext is None:
        _ext = load_inline(
            name="eigh_bjac_ext",
            cpp_sources=_CPP_SRC,
            cuda_sources=_CUDA_SRC,
            functions=["bjac"],
            verbose=False,
            extra_cuda_cflags=["-O3"],
        )
    return _ext


def _bjac_eigh(A):
    b, n = A.shape[0], A.shape[-1]
    L, V = _get_ext().bjac(A.contiguous(), BJ_B, BJ_SWEEPS, BJ_INNER)
    L, order = torch.sort(L, dim=1)                 # ascending
    Q = torch.gather(V, 2, order.unsqueeze(1).expand(b, n, n))
    return Q, L


def custom_kernel(data: input_t) -> output_t:
    if not _FALLBACK:
        n = data.shape[-1]
        if 32 <= n <= BJ_MAX_N and n % BJ_B == 0:
            try:
                return _bjac_eigh(data)
            except Exception as ex:
                print(f"[eigh] bjac fell back to torch (n={n}): {ex}", flush=True)
    values, vectors = torch.linalg.eigh(data)       # fallback
    return vectors, values
scrolls · 182 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