#include "common.hpp" #include #include void check(cudaError_t err) { if(err!=cudaSuccess) throw std::runtime_error(cudaGetErrorString(err)); } // std::string in host Args must never be copied into a device kernel. struct DeviceArgs { uint64_t seed,seeds,width,height,capacity; int32_t x0,z0; int n; }; __device__ void emit(DeviceArgs a,show::Hit* hits,unsigned long long* count, uint64_t seed,int32_t x,int32_t z) { const auto slot=atomicAdd(count,1ULL); if(slot hits; }; Run run(int mode,const show::Args& a,int maxBlocks,Buffers& b) { const auto wall=std::chrono::steady_clock::now(); check(cudaMemset(b.count,0,sizeof(unsigned long long))); const DeviceArgs d{a.seed,a.seeds,a.width,a.height,a.capacity,a.x0,a.z0,a.n}; const int threads=256; const uint64_t work=mode==2 ? a.seeds : a.positions(); const int blocks=int(std::min((work+threads-1)/threads,maxBlocks)); check(cudaEventRecord(b.begin)); switch(mode) { case 2: seedKernel<<>>(d,b.hits,b.count); break; case 3: launchCoordinate(blocks,threads,d,b.hits,b.count); break; case 4: { const uint64_t tiles=(a.width+15)/16*((a.height+15)/16); if((a.height+15)/16<=65535) { const dim3 grid(unsigned((a.width+15)/16),unsigned((a.height+15)/16)); outerRectKernel<<>>(d,b.hits,b.count); } else { outerKernel<<(tiles,0x7FFFFFFFULL)),dim3(16,16)>>>(d,b.hits,b.count); } break; } default: throw std::runtime_error("mode must be 2..4"); } check(cudaGetLastError()); check(cudaEventRecord(b.end)); check(cudaEventSynchronize(b.end)); float kernel=0; check(cudaEventElapsedTime(&kernel,b.begin,b.end)); unsigned long long count=0; check(cudaMemcpy(&count,b.count,sizeof(count),cudaMemcpyDeviceToHost)); if(count>a.capacity) throw std::runtime_error("hit buffer overflow; raise --capacity"); std::vector hits(count); if(count) check(cudaMemcpy(hits.data(),b.hits,count*sizeof(show::Hit),cudaMemcpyDeviceToHost)); const double wallMs=show::ms(wall); std::sort(hits.begin(),hits.end()); // equality checks are outside timed scan return {kernel,wallMs,std::move(hits)}; } int main(int argc,char** argv) { try { const auto start=std::chrono::steady_clock::now(); const auto a=show::parse(argc,argv); std::vector modes; if(a.modes=="all") modes={2,3,4}; else { for(size_t p=0;p4 || std::find(modes.begin(),modes.end(),int(m))!=modes.end()) throw std::runtime_error("invalid/duplicate CUDA mode"); modes.push_back(int(m)); if(end==std::string::npos) break; p=end+1; } } if(modes.empty()) throw std::runtime_error("empty mode list"); int device=0; cudaDeviceProp prop{}; check(cudaGetDevice(&device)); check(cudaGetDeviceProperties(&prop,device)); const int maxBlocks=prop.multiProcessorCount*32; Buffers b; check(cudaMalloc(&b.hits,a.capacity*sizeof(show::Hit))); check(cudaMalloc(&b.count,sizeof(unsigned long long))); check(cudaEventCreate(&b.begin)); check(cudaEventCreate(&b.end)); const double setup=show::ms(start); std::vector expected; bool first=true; for(int mode:modes) { const auto r=run(mode,a,maxBlocks,b); // warm every kernel on actual workload if(first) { expected=r.hits; first=false; } else if(r.hits!=expected) throw std::runtime_error("CUDA warmups disagree"); } std::map> kernel,wall; for(int repeat=0;repeat