Test: Extend HIP PSF replay for production and sustained rounds
Add production, production-prepared, production-parallel and production-busy modes plus an optional 1..512 round count. Repeated rounds validate against a round-scaled CPU double reference and report per-round throughput; production-parallel exercises concurrent prepared-chunk submission and production-busy adds host spin workers to probe contention.
This commit is contained in:
1 parent
4bf2de1b40
commit
ce691b11ad
1 file changed
+82
-13
+82
-13
@@ -6,6 +6,8 @@
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <climits>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <sys/resource.h>
|
||||
|
||||
struct TileTask { unsigned tile, first, count, partial; };
|
||||
@@ -102,13 +104,26 @@ static size_t contributions(const std::vector<PsfCachedEvent>& events,int width,
|
||||
return n;
|
||||
}
|
||||
int main(int argc,char **argv) {
|
||||
if(argc<4 || argc>5) {puts("Usage: replay_psf INPUT.events EVENTS(1..65536) atomic|16|32|adaptive [disperse|mixed]\nExternal timeout <=45s; sequential processes only.");return 2;}
|
||||
if(argc<4 || argc>6) {puts("Usage: replay_psf INPUT.events EVENTS(1..65536) atomic|16|32|adaptive|production|production-prepared|production-parallel|production-busy [disperse|mixed] [rounds=1..512]\nExternal timeout <=45s; sequential processes only. production-busy adds 15 CPU spin workers to test host contention. Repeated rounds compare against a scaled CPU double reference.");return 2;}
|
||||
char *end=nullptr;const long requested=strtol(argv[2],&end,10);
|
||||
if(!*argv[2] || *end || requested<1 || requested>65536) return 2;
|
||||
const bool adaptive=!strcmp(argv[3],"adaptive");
|
||||
const int requested_tile=!strcmp(argv[3],"atomic") ? 0 : !strcmp(argv[3],"16") ? 16 :
|
||||
const bool parallel_production=!strcmp(argv[3],"production-parallel");
|
||||
const bool busy_production=!strcmp(argv[3],"production-busy");
|
||||
const bool prepared_production=!strcmp(argv[3],"production-prepared");
|
||||
const bool production=!strcmp(argv[3],"production") || prepared_production || parallel_production || busy_production;
|
||||
const int requested_tile=(!strcmp(argv[3],"atomic") || production) ? 0 : !strcmp(argv[3],"16") ? 16 :
|
||||
!strcmp(argv[3],"32") ? 32 : adaptive ? 16 : -1;
|
||||
if(requested_tile<0 || (argc==5 && strcmp(argv[4],"disperse") && strcmp(argv[4],"mixed")))return 2;
|
||||
const bool distribution_arg=argc>=5 && (!strcmp(argv[4],"disperse") || !strcmp(argv[4],"mixed"));
|
||||
if(requested_tile<0 || (argc==6 && !distribution_arg))return 2;
|
||||
const char *rounds_arg=argc==6 ? argv[5] : argc==5 && !distribution_arg ? argv[4] : nullptr;
|
||||
long rounds=1;
|
||||
if(rounds_arg) {
|
||||
char *rounds_end=nullptr;
|
||||
rounds=strtol(rounds_arg,&rounds_end,10);
|
||||
if(!*rounds_arg || *rounds_end || rounds<1 || rounds>512)return 2;
|
||||
}
|
||||
if(parallel_production && (rounds<16 || rounds%16 || distribution_arg)) return 2;
|
||||
FILE *f=fopen(argv[1],"r");if(!f){perror(argv[1]);return 2;}
|
||||
int width,height;size_t count;PointSpreadFunction psf;double tail;
|
||||
if(fscanf(f,"PSFEVENTS1 %d %d %zu %lf %lf %lf",&width,&height,&count,&psf.fwhm_pixels,&psf.moffat_beta,&tail)!=6 ||
|
||||
@@ -127,8 +142,8 @@ int main(int argc,char **argv) {
|
||||
fclose(f);
|
||||
// Wing-clipped events retain their physical radius; traversal alone is cache-limited.
|
||||
const size_t original_contributions=contributions(events,width,height,cache);
|
||||
const bool mixed=argc==5 && !strcmp(argv[4],"mixed");
|
||||
if(argc==5 && !mixed) {
|
||||
const bool mixed=distribution_arg && !strcmp(argv[4],"mixed");
|
||||
if(distribution_arg && !mixed) {
|
||||
unsigned state=12345;
|
||||
for(auto&e:events) {
|
||||
// Preserve fractional phase and clipping: move only fully interior events.
|
||||
@@ -154,7 +169,7 @@ int main(int argc,char **argv) {
|
||||
start=omp_get_wtime();if(hip_psf_sink_create(&s,width,height,&cache,16384,message,sizeof message)){fprintf(stderr,"%s\n",message);return 1;}
|
||||
const double create=omp_get_wtime()-start;
|
||||
hipDeviceProp_t prop;check(hipGetDeviceProperties(&prop,0));
|
||||
printf("device=%s arch=%s events=%zu frame=%dx%d mode=%s distribution=%s contributions=%zu CPU_reference=%.9f create=%.9f\n",prop.name,prop.gcnArchName,events.size(),width,height,argv[3],mixed?"fixture-mixed-direct":argc==5?"synthetic-phase-preserving-dispersal":"captured",work,cpu_time,create);fflush(stdout);
|
||||
printf("device=%s arch=%s events=%zu rounds=%ld frame=%dx%d mode=%s distribution=%s contributions_per_round=%zu CPU_reference=%.9f create=%.9f\n",prop.name,prop.gcnArchName,events.size(),rounds,width,height,argv[3],mixed?"fixture-mixed-direct":distribution_arg?"synthetic-phase-preserving-dispersal":"captured",work,cpu_time,create);fflush(stdout);
|
||||
double select=0,bin=0,upload=0,prepare=0,kernel=0,merge=0,clear=0;
|
||||
size_t peak_scratch=0,ref_total=0,task_total=0,center_tiles=0,tile_chunks=0,atomic_chunks=0;
|
||||
Prepared *prepared=nullptr;int *bounds=nullptr;
|
||||
@@ -166,6 +181,17 @@ int main(int argc,char **argv) {
|
||||
if(hip_psf_sink_load_hdr(s,gpu.data(),message,sizeof message)) {fprintf(stderr,"%s\n",message);exit(1);}
|
||||
};
|
||||
const double replay_start=omp_get_wtime();
|
||||
std::atomic<bool> stop_busy(false);
|
||||
std::vector<std::thread> busy_workers;
|
||||
if(busy_production) for(unsigned worker=0;worker<15;++worker)
|
||||
busy_workers.emplace_back([&,worker] {
|
||||
volatile uint64_t state=worker+1;
|
||||
while(!stop_busy.load(std::memory_order_relaxed))
|
||||
for(int i=0;i<4096;++i)state=state*1664525u+1013904223u;
|
||||
});
|
||||
HipPsfPreparedChunk *serial_chunk=nullptr;
|
||||
if(prepared_production && hip_psf_prepared_chunk_create(
|
||||
&serial_chunk,s,message,sizeof message)) {fprintf(stderr,"%s\n",message);return 1;}
|
||||
if(requested_tile) {
|
||||
// Fixed hard bounds; no full-frame event list or per-chunk allocation on GPU.
|
||||
check(hipMalloc(&tile_events,16384*sizeof(PsfCachedEvent)));
|
||||
@@ -174,7 +200,34 @@ int main(int argc,char **argv) {
|
||||
check(hipMalloc(&drefs,32*1024*1024));check(hipMalloc(&dtasks,2*1024*1024));
|
||||
check(hipMalloc(&dstarts,131072));check(hipMalloc(&partial,128*1024*1024));
|
||||
}
|
||||
if(parallel_production) {
|
||||
omp_lock_t lock; omp_init_lock(&lock);
|
||||
int failed=0;
|
||||
#pragma omp parallel num_threads(16) reduction(+:failed)
|
||||
{
|
||||
char local_message[256]={};
|
||||
HipPsfPreparedChunk *chunk=nullptr;
|
||||
if(hip_psf_prepared_chunk_create(&chunk,s,local_message,sizeof local_message)) {
|
||||
fprintf(stderr,"parallel chunk create: %s\n",local_message);failed=1;
|
||||
}
|
||||
for(long round=0;round<rounds/omp_get_num_threads() && !failed;++round)
|
||||
for(size_t offset=0;offset<events.size();offset+=16384) {
|
||||
const size_t n=std::min((size_t)16384,events.size()-offset);
|
||||
if(hip_psf_prepared_chunk_prepare(chunk,events.data()+offset,n,
|
||||
local_message,sizeof local_message)) {
|
||||
fprintf(stderr,"parallel prepare: %s\n",local_message);failed=1;break;
|
||||
}
|
||||
omp_set_lock(&lock);
|
||||
const int result=hip_psf_sink_submit_prepared(s,chunk,events.data()+offset,n,
|
||||
local_message,sizeof local_message);
|
||||
omp_unset_lock(&lock);
|
||||
if(result) {fprintf(stderr,"parallel submit: %s\n",local_message);failed=1;break;}
|
||||
}
|
||||
hip_psf_prepared_chunk_destroy(chunk);
|
||||
}
|
||||
omp_destroy_lock(&lock);
|
||||
if(failed)return 1;
|
||||
} else for(long round=0;round<rounds;++round) for(size_t offset=0;offset<events.size();offset+=16384) {
|
||||
const size_t n=std::min((size_t)16384,events.size()-offset);
|
||||
int tile=requested_tile;
|
||||
if(adaptive) {
|
||||
@@ -197,8 +250,16 @@ int main(int argc,char **argv) {
|
||||
center_tiles+=count_occupied;select+=omp_get_wtime()-start;
|
||||
}
|
||||
if(!tile) {
|
||||
++atomic_chunks;
|
||||
if(hip_psf_sink_submit(s,events.data()+offset,n,message,sizeof message)) {fprintf(stderr,"%s\n",message);return 1;}
|
||||
if(!production)++atomic_chunks;
|
||||
if(prepared_production && hip_psf_prepared_chunk_prepare(
|
||||
serial_chunk,events.data()+offset,n,message,sizeof message)) {
|
||||
fprintf(stderr,"%s\n",message);return 1;
|
||||
}
|
||||
const int submit_result=prepared_production
|
||||
? hip_psf_sink_submit_prepared(s,serial_chunk,events.data()+offset,n,
|
||||
message,sizeof message)
|
||||
: hip_psf_sink_submit(s,events.data()+offset,n,message,sizeof message);
|
||||
if(submit_result) {fprintf(stderr,"%s\n",message);return 1;}
|
||||
if(mixed && offset==0)direct_boundary();continue;
|
||||
}
|
||||
++tile_chunks;
|
||||
@@ -255,22 +316,30 @@ int main(int argc,char **argv) {
|
||||
// Every partial pixel is written by exactly one thread: no memset required.
|
||||
}
|
||||
if(hip_psf_sink_finish(s,gpu.data(),message,sizeof message)){fprintf(stderr,"%s\n",message);return 1;}
|
||||
stop_busy.store(true,std::memory_order_relaxed);
|
||||
for(auto &worker:busy_workers)worker.join();
|
||||
hip_psf_prepared_chunk_destroy(serial_chunk);
|
||||
if(requested_tile) {start=omp_get_wtime();check(hipFree(tile_events));check(hipFree(prepared));check(hipFree(bounds));check(hipFree(drefs));check(hipFree(dtasks));check(hipFree(dstarts));check(hipFree(partial));clear=omp_get_wtime()-start;}
|
||||
const double wall=omp_get_wtime()-replay_start;
|
||||
HipPsfTiming timing={};hip_psf_sink_get_timing(s,&timing);
|
||||
upload+=timing.upload_seconds;kernel+=timing.kernel_seconds;
|
||||
if(production) {
|
||||
select+=timing.selection_seconds;bin+=timing.bin_seconds;
|
||||
atomic_chunks+=timing.atomic_batch_count;tile_chunks+=timing.tile_batch_count;
|
||||
}
|
||||
double max_abs=0,max_rel=0;long double sums[3]={},diffs[3]={};bool pass=true;
|
||||
for(size_t i=0;i<values;++i) {
|
||||
if(!std::isfinite(cpu[i]) || !std::isfinite(gpu[i]))pass=false;
|
||||
const double d=fabs(gpu[i]-cpu[i]);max_abs=fmax(max_abs,d);
|
||||
if(cpu[i]!=0)max_rel=fmax(max_rel,d/fabs(cpu[i]));
|
||||
sums[i%3]+=cpu[i];diffs[i%3]+=(long double)gpu[i]-cpu[i];
|
||||
const double reference=cpu[i]*rounds;
|
||||
const double d=fabs(gpu[i]-reference);max_abs=fmax(max_abs,d);
|
||||
if(reference!=0)max_rel=fmax(max_rel,d/fabs(reference));
|
||||
sums[i%3]+=reference;diffs[i%3]+=(long double)gpu[i]-reference;
|
||||
}
|
||||
long double ysum=.2126L*sums[0]+.7152L*sums[1]+.0722L*sums[2],ydiff=.2126L*diffs[0]+.7152L*diffs[1]+.0722L*diffs[2];
|
||||
struct rusage usage;getrusage(RUSAGE_SELF,&usage);
|
||||
printf("complete_replay=%.9f replay_wall=%.9f select=%.9f bin=%.9f upload=%.9f prepare=%.9f accumulation=%.9f merge=%.9f download=%.9f cleanup=%.9f events_s=%.3f contributions_s=%.3f center_tiles=%zu tile_chunks=%zu atomic_chunks=%zu refs=%zu tasks=%zu scratch_used_peak=%zu scratch_device_reserved=%zu RSS_KiB=%ld max_abs=%.17g max_rel=%.17g flux_R=%.17Lg flux_G=%.17Lg flux_B=%.17Lg flux_Y=%.17Lg\n",create+wall,wall,select,bin,upload,prepare,kernel,merge,timing.download_seconds,clear,events.size()/wall,work/wall,center_tiles,tile_chunks,atomic_chunks,ref_total,task_total,peak_scratch,requested_tile?163*1024*1024+131072+16384*(sizeof(Prepared)+(size_t)(2*cache.radius_pixels+1)*2*sizeof(int)):0,usage.ru_maxrss,max_abs,max_rel,sums[0]?diffs[0]/sums[0]:0,sums[1]?diffs[1]/sums[1]:0,sums[2]?diffs[2]/sums[2]:0,ysum?ydiff/ysum:0);
|
||||
printf("complete_replay=%.9f replay_wall=%.9f select=%.9f bin=%.9f upload=%.9f prepare=%.9f accumulation=%.9f merge=%.9f download=%.9f cleanup=%.9f events_s=%.3f contributions_s=%.3f center_tiles=%zu tile_chunks=%zu atomic_chunks=%zu refs=%zu tasks=%zu scratch_used_peak=%zu scratch_device_reserved=%zu RSS_KiB=%ld max_abs=%.17g max_rel=%.17g flux_R=%.17Lg flux_G=%.17Lg flux_B=%.17Lg flux_Y=%.17Lg validation=%s\n",create+wall,wall,select,bin,upload,prepare,kernel,merge,timing.download_seconds,clear,events.size()*rounds/wall,work*rounds/wall,center_tiles,tile_chunks,atomic_chunks,ref_total,task_total,peak_scratch,requested_tile?163*1024*1024+131072+16384*(sizeof(Prepared)+(size_t)(2*cache.radius_pixels+1)*2*sizeof(int)):0,usage.ru_maxrss,max_abs,max_rel,sums[0]?diffs[0]/sums[0]:0,sums[1]?diffs[1]/sums[1]:0,sums[2]?diffs[2]/sums[2]:0,ysum?ydiff/ysum:0,"scaled-cpu-double-reference");
|
||||
for(int c=0;c<3;++c)if(fabsl(diffs[c])>1e-10L*fmaxl(fabsl(sums[c]),1e-30L))pass=false;
|
||||
if(fabsl(ydiff)>1e-10L*fmaxl(fabsl(ysum),1e-30L))pass=false;
|
||||
hip_psf_sink_destroy(s);psf_kernel_cache_destroy(&cache);
|
||||
return pass && max_abs<1e-10 && max_rel<1e-10 ? 0 : 1;
|
||||
return pass && max_rel<1e-10 && (rounds>1 || max_abs<1e-10) ? 0 : 1;
|
||||
}
|
||||
Reference in new issue
Block a user