#include "interval_solver.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace sr = slime_research; namespace fs = full_search_experiment; using Clock = std::chrono::steady_clock; #define CUDA(call) do { auto e = (call); if (e != cudaSuccess) throw std::runtime_error(std::string(#call)+": "+cudaGetErrorString(e)); } while (0) struct Position { int32_t x, z; uint64_t anchor; sr::CellTerm cells[25]; }; struct Job { uint32_t position, root; }; struct Match { uint64_t position, seed; bool operator<(const Match& o)const{return std::tie(position,seed) __global__ void rootKernel(const Position* positions,uint32_t count,const uint32_t* good, uint32_t* counts,Job* jobs,unsigned long long* total,bool collect) { uint64_t idx=uint64_t(blockIdx.x)*blockDim.x+threadIdx.x; if(idx >= (uint64_t(count)<<18)) return; const uint32_t pos=uint32_t(idx>>18), root=uint32_t(idx)&((1U<<18)-1); const Position& p=positions[pos]; for(int i=0;i<25;++i){ const uint32_t in=root+uint32_t(p.cells[i].term); if constexpr(Mode==0){ if((((in^uint32_t(sr::kCombinedXor))*uint32_t(sr::kJavaMultiplier)+11U)>>17)&1U)return; }else{ const uint32_t k=in&((1U<<18)-1); if(((good[k>>5]>>(k&31))&1U)==0)return; } } atomicAdd(counts+pos,1U); const auto at=atomicAdd(total,1ULL); if(collect&&at __global__ void searchKernel(const Position* positions,const Job* jobs,uint32_t count, Match* out,unsigned long long* total) { uint64_t idx=uint64_t(blockIdx.x)*blockDim.x+threadIdx.x; if(idx >= (uint64_t(count)<<17))return; const Job job=jobs[idx>>17]; uint64_t low=job.root+((idx&((1U<<17)-1))<<18); const Position& p=positions[job.position]; Emitter emit{job.position,p.anchor,out,total}; if constexpr(Mode==0)sr::solveUpper13(low,p.cells,25,emit); else if constexpr(Mode==1)fs::solveIntervals(low,p.cells,25,emit); else if constexpr(Mode==2){if(fs::differenceFilter(low,p.cells,25))sr::solveUpper13(low,p.cells,25,emit);} else if constexpr(Mode==3){if(fs::differenceFilter(low,p.cells,25))fs::solveIntervals(low,p.cells,25,emit);} } __global__ void tailKernel(const Position* positions,uint32_t count,Match* out,unsigned long long* total,int cellCount){ const uint64_t idx=uint64_t(blockIdx.x)*blockDim.x+threadIdx.x; if(idx >= uint64_t(count)*cellCount*(1U<<20))return; const uint64_t cell=idx>>20; const uint32_t pos=cell/cellCount,i=cell%cellCount; const uint64_t v=(uint64_t(2147483640U)<<17)+(idx&((1U<<20)-1)); const Position& p=positions[pos]; const uint64_t u=((v-11)*0xDFE05BCB1365ULL)&sr::kMask48; const uint64_t seed=((u^sr::kCombinedXor)-p.cells[i].term)&sr::kMask48; if(sr::verifyPattern(seed,p.cells,cellCount)){ Emitter e{pos,p.anchor,out,total};e(seed); } } // If an output is odd, a true solid-pattern seed must reject that output. // Pick the first odd cell: four tail states correspond to each seed-low18. // When every output is even, enumerate all cells' four tail states instead. // This covers multiple-rejection paths too, without assuming they are impossible. __global__ void canonicalTailKernel(const Position* positions,uint32_t count,Match* out,unsigned long long* total,int cellCount){ const uint64_t idx=uint64_t(blockIdx.x)*blockDim.x+threadIdx.x; if(idx >= (uint64_t(count)<<18))return; const uint32_t pos=idx>>18,root=idx&((1U<<18)-1); const Position& p=positions[pos]; int firstOdd=-1; for(int i=0;i>17)&1U){firstOdd=i;break;} const int begin=firstOdd>=0?firstOdd:0,end=firstOdd>=0?firstOdd+1:cellCount; for(int i=begin;i>17)+2*k)<<17)+(vlow&((1U<<17)-1)); const uint64_t u=((v-11)*0xDFE05BCB1365ULL)&sr::kMask48; const uint64_t seed=((u^sr::kCombinedXor)-p.cells[i].term)&sr::kMask48; if(sr::verifyPattern(seed,p.cells,cellCount)){Emitter e{pos,p.anchor,out,total};e(seed);} } } } struct Device { Position* positions=nullptr; Job* jobs=nullptr; Match* matches=nullptr; uint32_t* counts=nullptr;uint32_t* good=nullptr;unsigned long long* total=nullptr; cudaEvent_t start,stop; Device(const std::vector& p){ CUDA(cudaMalloc(&positions,p.size()*sizeof(Position)));CUDA(cudaMemcpy(positions,p.data(),p.size()*sizeof(Position),cudaMemcpyHostToDevice)); CUDA(cudaMalloc(&jobs,JOB_CAPACITY*sizeof(Job)));CUDA(cudaMalloc(&matches,MATCH_CAPACITY*sizeof(Match))); CUDA(cudaMalloc(&counts,p.size()*sizeof(uint32_t)));CUDA(cudaMalloc(&total,sizeof(*total))); std::array bits{}; for(uint32_t k=0;k<(1U<<18);++k)if(!((((k^uint32_t(sr::kCombinedXor))*uint32_t(sr::kJavaMultiplier)+11U)>>17)&1U))bits[k>>5]|=1U<<(k&31); CUDA(cudaMalloc(&good,sizeof(bits)));CUDA(cudaMemcpy(good,bits.data(),sizeof(bits),cudaMemcpyHostToDevice)); CUDA(cudaEventCreate(&start));CUDA(cudaEventCreate(&stop)); } ~Device(){cudaFree(positions);cudaFree(jobs);cudaFree(matches);cudaFree(counts);cudaFree(total);cudaFree(good);cudaEventDestroy(start);cudaEventDestroy(stop);} float finish(){CUDA(cudaGetLastError());CUDA(cudaEventRecord(stop));CUDA(cudaEventSynchronize(stop));float ms;CUDA(cudaEventElapsedTime(&ms,start,stop));return ms;} uint64_t totalCount(){unsigned long long n;CUDA(cudaMemcpy(&n,total,sizeof(n),cudaMemcpyDeviceToHost));return n;} float roots(uint32_t n,int mode,bool collect){ CUDA(cudaMemset(total,0,sizeof(*total)));CUDA(cudaMemset(counts,0,n*sizeof(uint32_t)));CUDA(cudaEventRecord(start)); unsigned blocks=unsigned(((uint64_t(n)<<18)+255)/256); if(mode==0)rootKernel<0><<>>(positions,n,good,counts,jobs,total,collect); else rootKernel<1><<>>(positions,n,good,counts,jobs,total,collect); return finish(); } float search(uint32_t n,int mode){ CUDA(cudaMemset(total,0,sizeof(*total)));CUDA(cudaEventRecord(start)); unsigned blocks=unsigned(((uint64_t(n)<<17)+255)/256); if(blocks){ if(mode==0)searchKernel<0><<>>(positions,jobs,n,matches,total); if(mode==1)searchKernel<1><<>>(positions,jobs,n,matches,total); if(mode==2)searchKernel<2><<>>(positions,jobs,n,matches,total); if(mode==3)searchKernel<3><<>>(positions,jobs,n,matches,total); } return finish(); } std::vector results(){ uint64_t n=totalCount();if(n>MATCH_CAPACITY)throw std::runtime_error("match overflow"); std::vector r(n);CUDA(cudaMemcpy(r.data(),matches,n*sizeof(Match),cudaMemcpyDeviceToHost));std::sort(r.begin(),r.end());return r; } }; int main(int argc,char**argv){ try{ if(argc<3)throw std::runtime_error("usage: cuda_bench positions.txt root|compare|fast|tail|complete [repeats=5] [tail-cells=25]"); std::ifstream in(argv[1]);if(!in)throw std::runtime_error("cannot open positions"); int32_t x,z;std::vector positions;while(in>>x>>z)positions.push_back(makePosition(x,z)); if(positions.empty()||positions.size()>65536)throw std::runtime_error("position count must be 1..65536"); std::string task=argv[2];int repeats=argc>3?std::stoi(argv[3]):5; int tailCells=argc>4?std::stoi(argv[4]):25; if(tailCells<1||tailCells>25)throw std::runtime_error("tail-cells must be 1..25"); auto processStart=Clock::now();Device d(positions); std::cout< referenceCounts; std::vector referenceJobs; auto collectRoots=[&](int mode){ float ms=d.roots(positions.size(),mode,true);uint64_t total=d.totalCount();if(total>JOB_CAPACITY)throw std::runtime_error("job overflow"); std::vector counts(positions.size());CUDA(cudaMemcpy(counts.data(),d.counts,counts.size()*sizeof(uint32_t),cudaMemcpyDeviceToHost)); std::vector jobs(total);CUDA(cudaMemcpy(jobs.data(),d.jobs,total*sizeof(Job),cudaMemcpyDeviceToHost)); std::sort(jobs.begin(),jobs.end(),[](Job a,Job b){return std::tie(a.position,a.root) expected; for(int r=-1;r0x7fffffffULL)throw std::runtime_error("tail grid too large"); unsigned blocks=unsigned((work+255)/256); if(mode==0)tailKernel<<>>(d.positions,positions.size(),d.matches,d.total,tailCells); else canonicalTailKernel<<>>(d.positions,positions.size(),d.matches,d.total,tailCells); float ms=d.finish();auto result=d.results(); result.erase(std::unique(result.begin(),result.end()),result.end()); if(r==-1&&mode==0)expected=result;else if(result!=expected)throw std::runtime_error("tail mismatch"); if(r>=0)std::cout<<"{\"type\":\"tail_timing\",\"mode\":"<(Clock::now()-processStart).count()<<"}"< expected; for(int r=-1;r>>(d.positions,positions.size(),d.matches,d.total,25); const float tailMs=d.finish();auto tail=d.results(); result.insert(result.end(),tail.begin(),tail.end());std::sort(result.begin(),result.end()); result.erase(std::unique(result.begin(),result.end()),result.end()); if(r==-1)expected=result;else if(result!=expected)throw std::runtime_error("complete pipeline result mismatch"); if(r>=0)std::cout<<"{\"type\":\"complete_timing\",\"round\":"<(Clock::now()-wallStart).count() <<",\"matches\":"< expected; const std::vector modes=task=="compare"?std::vector{0,1,2,3}:std::vector{1,2,3}; bool first=true; for(int m:modes){d.search(referenceJobs.size(),m);auto r=d.results();if(first){expected=r;first=false;}else if(r!=expected)throw std::runtime_error("warmup result mismatch");} for(int r=0;r(Clock::now()-wallStart).count()<<",\"matches\":"<(64,positions.size());++n){ size_t pos=n*positions.size()/std::min(64,positions.size());std::vector cpu,gpu; for(uint32_t s=0;s<(1U<<18);++s)if(sr::partialPatternPossible(s,18,positions[pos].cells,25))cpu.push_back(s); for(auto j:referenceJobs)if(j.position==pos)gpu.push_back(j.root); if(cpu!=gpu)throw std::runtime_error("CPU/GPU roots mismatch"); } std::map> classes; size_t offset=0; for(size_t pos=0;pos roots; while(offsetsecond!=roots)throw std::runtime_error("periodic class root list mismatch"); } std::cout<<"{\"type\":\"periodic_validation\",\"classes\":"<(Clock::now()-processStart).count()<<"}"<