Buckets:
| """Bench the custom int4 W4A16 GEMM (M=16,K=2560,N=10240,g32) vs roofline + torch. | |
| Achieved GB/s tells us if a bespoke GEMM can play with Marlin (~78% roofline).""" | |
| import ctypes, time, torch | |
| try: | |
| from cuda.bindings import driver as cu, nvrtc | |
| except ImportError: | |
| from cuda import cuda as cu, nvrtc # type: ignore | |
| torch.manual_seed(0); DEV="cuda" | |
| M,K,N,G=16,2560,10240,32 | |
| def ck(r): | |
| e=r[0]; ok=(e.value==0) if hasattr(e,'value') else (int(e)==0) | |
| if not ok: raise RuntimeError(str(r)) | |
| return r[1:] if len(r)>2 else (r[1] if len(r)==2 else None) | |
| def compile(): | |
| src=open("w4gemm.cu","rb").read(); p=ck(nvrtc.nvrtcCreateProgram(src,b"w4gemm.cu",0,[],[])) | |
| o=[b"--gpu-architecture=compute_86",b"--std=c++17",b"-default-device"] | |
| r=nvrtc.nvrtcCompileProgram(p,len(o),o); l=ck(nvrtc.nvrtcGetProgramLogSize(p)); b=b" "*l | |
| nvrtc.nvrtcGetProgramLog(p,b); t=b.decode(errors="replace").strip() | |
| if t and t!="\x00": print("[nvrtc]",t[:1200]) | |
| e=r[0] | |
| if (e.value!=0) if hasattr(e,'value') else (int(e)!=0): raise RuntimeError("compile fail") | |
| s=ck(nvrtc.nvrtcGetPTXSize(p)); ptx=b" "*s; nvrtc.nvrtcGetPTX(p,ptx); return ptx | |
| def main(): | |
| torch.zeros(1,device=DEV); ck(cu.cuInit(0)) | |
| x=(torch.randn(M,K,device=DEV)*0.3).to(torch.bfloat16) | |
| # int4 weights [N,K] symmetric, group-32 scale | |
| qint=torch.randint(-8,8,(N,K),device=DEV,dtype=torch.int32) | |
| scale=(torch.rand(N,K//G,device=DEV)*0.02+0.005).to(torch.bfloat16) | |
| # pack int4: 8 per u32 along K | |
| qb=(qint+8).to(torch.int32).reshape(N,K//8,8) | |
| packed=torch.zeros(N,K//8,dtype=torch.int32,device=DEV) | |
| for i in range(8): packed|=(qb[:,:,i]<<(4*i)) | |
| packed=packed.to(torch.int32) | |
| # reference: dequant W -> bf16 matmul | |
| wdq=(qint.float().reshape(N,K//G,G)*scale.float().unsqueeze(-1)).reshape(N,K).to(torch.bfloat16) | |
| ref=(x.float()@wdq.float().T).to(torch.bfloat16) | |
| y=torch.zeros(M,N,dtype=torch.bfloat16,device=DEV) | |
| mod=ck(cu.cuModuleLoadData(compile())); fn=ck(cu.cuModuleGetFunction(mod,b"w4gemm")) | |
| ptab=torch.zeros(8,dtype=torch.int64,device=DEV); host=torch.zeros(8,dtype=torch.int64) | |
| host[0]=x.data_ptr();host[1]=packed.data_ptr();host[2]=scale.data_ptr();host[3]=y.data_ptr() | |
| ptab.copy_(host) | |
| n_sm=torch.cuda.get_device_properties(0).multi_processor_count | |
| grid=n_sm*2 | |
| smem=M*K*2 | |
| a0=ctypes.c_void_p(ptab.data_ptr()); a1=ctypes.c_int(K); a2=ctypes.c_int(N); a3=ctypes.c_int(G) | |
| arr=(ctypes.c_void_p*4)(*[ctypes.cast(ctypes.pointer(z),ctypes.c_void_p) for z in (a0,a1,a2,a3)]) | |
| ck(cu.cuFuncSetAttribute(fn, cu.CUfunction_attribute.CU_FUNC_ATTRIBUTE_MAX_DYNAMIC_SHARED_SIZE_BYTES, smem)) | |
| def launch(): | |
| st=torch.cuda.current_stream().cuda_stream | |
| ck(cu.cuLaunchKernel(fn,grid,1,1,256,1,1,smem,st,ctypes.addressof(arr),0)) | |
| launch(); torch.cuda.synchronize() | |
| diff=(y.float()-ref.float()).abs().max().item(); rel=diff/(ref.abs().max().item()+1e-9) | |
| print(f"[w4] max|diff|={diff:.4e} rel={rel:.4e} {'OK' if rel<0.05 else 'WRONG'}") | |
| for _ in range(20): launch() | |
| torch.cuda.synchronize(); t0=time.perf_counter() | |
| for _ in range(200): launch() | |
| torch.cuda.synchronize(); us=(time.perf_counter()-t0)/200*1e6 | |
| wbytes=N*(K//2) + N*(K//G)*2 # int4 weights + bf16 scales | |
| bw=wbytes/(us*1e-6)/1e9 | |
| print(f"[w4] {us:.2f} us/GEMM achieved {bw:.0f} GB/s ({100*bw/600:.0f}% of 600 roofline; Marlin~78%)") | |
| print(f"[w4] weight bytes {wbytes/1e6:.1f}MB roofline {wbytes/600e9*1e6:.2f}us @600GB/s") | |
| if __name__=="__main__": main() | |
Xet Storage Details
- Size:
- 3.54 kB
- Xet hash:
- 2bf36f9ef5208314c82dfefd0b02fec6d79f9b7bd62397a1fe80d664a9b17c2b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.