// Why is torch's int8 relu faster in the L2 regime? Hypothesis: per-thread byte // granularity (more threads = more MLP). Sweep bytes/thread {4,8,16} x block, int8, // at 16.7M (33.4MB ws, L2-resident). Uses __vmaxs4. Ranks configs in one process. #include #include #include #include #include #include #include #define CK(x) do{cudaError_t e=(x); if(e){printf("ERR %s\n",cudaGetErrorString(e));exit(1);}}while(0) // 4 bytes/thread (like torch: 4 int8 per thread) __global__ void k4(unsigned* __restrict__ o,const unsigned* __restrict__ in,int64_t nu){ int64_t s=(int64_t)gridDim.x*blockDim.x; for(int64_t i=(int64_t)blockIdx.x*blockDim.x+threadIdx.x;i h(N); for(int64_t i=0;i run;}; std::vector cs; for(int blk: {64,128,256,512}){ cs.push_back({"4B/thread blk"+std::to_string(blk),4,blk,[=](int b){ int64_t nu=bytes/4,g=(nu+b-1)/b; k4<<<(unsigned)std::min(g,1<<22),b>>>((unsigned*)doo,(const unsigned*)di,nu);} }); cs.push_back({"8B/thread blk"+std::to_string(blk),8,blk,[=](int b){ int64_t nu=bytes/8,g=(nu+b-1)/b; k8<<<(unsigned)std::min(g,1<<22),b>>>((uint2*)doo,(const uint2*)di,nu);} }); cs.push_back({"16B/thread blk"+std::to_string(blk),16,blk,[=](int b){ int64_t nu=bytes/16,g=(nu+b-1)/b; k16<<<(unsigned)std::min(g,1<<22),b>>>((int4*)doo,(const int4*)di,nu);} }); } std::string best;double bbw=0; for(auto&c:cs){ for(int w=0;w<100;w++) c.run(c.block); CK(cudaDeviceSynchronize()); // min over a few timed batches to get the least-throttled estimate double bestms=1e9; for(int rep=0;rep<5;rep++){ cudaEvent_t a,b;cudaEventCreate(&a);cudaEventCreate(&b);cudaEventRecord(a); for(int it=0;it<200;it++) c.run(c.block); cudaEventRecord(b);CK(cudaEventSynchronize(b)); float ms;cudaEventElapsedTime(&ms,a,b);ms/=200; bestms=std::min(bestms,(double)ms); cudaEventDestroy(a);cudaEventDestroy(b); } double bw=gb/(bestms/1e3); printf(" %-18s %.0f GB/s (%.1f%% peak)\n",c.name.c_str(),bw,100*bw/peak); if(bw>bbw){bbw=bw;best=c.name;} } printf(">> BEST: %s %.0f GB/s\n",best.c_str(),bbw); return 0; }