-rwxr-xr-x 7333 djbsort-20260127/sortverif/verifymany raw
#!/usr/bin/env python3 import os import sys import multiprocessing import subprocess import traceback import resource from datetime import datetime,timezone compiler = os.getenv('CC','gcc')+' '+os.getenv('CFLAGS','') top = datetime.now(timezone.utc).strftime('results-%Y%m%d-%H%M%S-UTC') top = f'{top}-pid{os.getpid()}' os.makedirs(top) try: os.remove('results') except FileNotFoundError: pass os.symlink(top,'results') try: os_threads = len(os.sched_getaffinity(0)) except AttributeError: os_threads = multiprocessing.cpu_count() os_threads = os.getenv('THREADS',default=os_threads) os_threads = int(os_threads) if os_threads < 1: os_threads = 1 resource.setrlimit(resource.RLIMIT_NOFILE,2*resource.getrlimit(resource.RLIMIT_NOFILE)[1:]) def cputime(): return resource.getrusage(resource.RUSAGE_SELF).ru_utime + resource.getrusage(resource.RUSAGE_CHILDREN).ru_utime # ===== handle library in non-system locations lib = 'libdjbsort.so' for libdir in os.getenv('LD_LIBRARY_PATH','').split(':'): for fn in f'{libdir}/{lib}',f'{libdir}/{lib}.1': if os.path.exists(fn): lib = fn break # ===== endianness os.makedirs(f'{top}/run',exist_ok=True) binary = f'{top}/run/endianness' with open(f'{binary}.c','w') as f: f.write('''#include <stdlib.h> #include <inttypes.h> int main() { uint16_t a = 0x0201; uint32_t b = 0x04030201; uint64_t c = 0x0807060504030201ULL; char *A = (char *) &a; char *B = (char *) &b; char *C = (char *) &c; if (A[0] == 1 && A[1] == 2 && B[0] == 1 && B[1] == 2 && B[2] == 3 && B[3] == 4 && C[0] == 1 && C[1] == 2 && C[2] == 3 && C[3] == 4 && C[4] == 5 && C[5] == 6 && C[6] == 7 && C[7] == 8) return 12; if (A[0] == 2 && A[1] == 1 && B[0] == 4 && B[1] == 3 && B[2] == 2 && B[3] == 1 && C[0] == 8 && C[1] == 7 && C[2] == 6 && C[3] == 5 && C[4] == 4 && C[5] == 3 && C[6] == 2 && C[7] == 1) return 21; return 99; } ''') command = f'{compiler} -o {binary} {binary}.c' subprocess.run(command.split(),check=True) endianness = subprocess.run([binary]).returncode endianness = {12:'littleendian',21:'bigendian'}[endianness] # ===== figure out implementations supported by emulated CPU # (not necessarily the same as the physical CPU; run under ./outsim) impls = {} for bits in 32,64: os.makedirs(f'{top}/run',exist_ok=True) binary = f'{top}/run/int{bits}-ic' with open(f'{binary}.c','w') as f: f.write(fr'''#include <stdio.h> #include <djbsort.h> int main() {{ long long numimpl = djbsort_numimpl_int{bits}(); for (long long i = 0;i < numimpl;++i) printf("%s %s\n",djbsort_dispatch_int{bits}_implementation(i),djbsort_dispatch_int{bits}_compiler(i)); fflush(stdout); return 0; }} ''') command = f'{compiler} -o {binary} {binary}.c -ldjbsort' subprocess.run(command.split(),check=True) command = f'./outsim {binary} {lib}' proc = subprocess.run(command.split(),stdout=subprocess.PIPE,check=True,universal_newlines=True) impls[bits] = proc.stdout.splitlines() # ===== main binaries for bits in 32,64: Ibytes = bits//8 os.makedirs(f'{top}/run',exist_ok=True) binary = f'{top}/run/int{bits}' with open(f'{binary}.c','w') as f: f.write(fr'''#include <unistd.h> #include <stdlib.h> #include <string.h> #include <djbsort.h> static int64_t ato64(const char *s) {{ int64_t result = 0; while (*s) result = result*10 + (*s++-'0'); return result; }} int main(int argc,char **argv) {{ if (!argv[1]) exit(111); int64_t n = ato64(argv[1]); int64_t impl = ato64(argv[2]); char *implname = argv[3]; char *implcompiler = argv[4]; if (strcmp(implname,djbsort_dispatch_int{bits}_implementation(impl))) exit(111); if (strcmp(implcompiler,djbsort_dispatch_int{bits}_compiler(impl))) exit(111); int{bits}_t *x = (int{bits}_t *) malloc(n*{Ibytes}); for (long long i = 0;i < n;++i) if (read(0,(char *) &x[i],{Ibytes}) != {Ibytes}) exit(111); djbsort_dispatch_int{bits}(impl)(x,n); for (long long i = 0;i < n;++i) if (write(1,(char *) &x[i],{Ibytes}) != {Ibytes}) exit(111); return 0; }} ''') command = f'{compiler} -o {binary} {binary}.c -ldjbsort' subprocess.run(command.split(),check=True) # ===== main driver def doit(arg): bits,n,i = arg binary = f'{top}/run/int{bits}' implname = impls[bits][i].split(' ')[0] implcompiler = ' '.join(impls[bits][i].split(' ')[1:]) runstarttime = cputime() result = '' for op,command in ( # beware: implcompiler normally has spaces ('unroll',['./unroll',f'{bits}',f'{n}',f'{i}',implname,implcompiler,binary,endianness,lib]), ('minmax',['./minmax',f'{bits}',f'{n}']), ('decompose',['./decompose',f'{bits}',f'{n}']), ): taskstarttime = cputime() try: proc = subprocess.run(command,input=result,stdout=subprocess.PIPE,stderr=subprocess.PIPE,universal_newlines=True) out,err = proc.stdout,proc.stderr except (OSError,FileNotFoundError): os.makedirs(f'{top}/{op}failed/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/{op}failed/int{bits}/impl{i}/{n}','w') as f: f.write(traceback.format_exc()) return bits,n,i,f'{op}failed' os.makedirs(f'{top}/{op}time/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/{op}time/int{bits}/impl{i}/{n}','w') as f: f.write(f'{cputime()-taskstarttime:.6f}\n') if len(out) > 0: os.makedirs(f'{top}/{op}stdout/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/{op}stdout/int{bits}/impl{i}/{n}','w') as f: f.write(out) if len(err) > 0: os.makedirs(f'{top}/{op}stderr/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/{op}stderr/int{bits}/impl{i}/{n}','w') as f: f.write(err) if proc.returncode != 0: os.makedirs(f'{top}/{op}failed/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/{op}failed/int{bits}/impl{i}/{n}','w') as f: f.write(f'exit code {proc.returncode}\n') return bits,n,i,f'{op}failed' result = out os.makedirs(f'{top}/successtime/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/successtime/int{bits}/impl{i}/{n}','w') as f: f.write(f'{cputime()-runstarttime:.6f}\n') os.makedirs(f'{top}/success/int{bits}/impl{i}',exist_ok=True) with open(f'{top}/success/int{bits}/impl{i}/{n}','w') as f: pass return bits,n,i,'success' for bits in 32,64: for i,impl in enumerate(impls[bits]): os.makedirs(f'{top}/impldata/int{bits}',exist_ok=True) with open(f'{top}/impldata/int{bits}/impl{i}','w') as f: f.write(impl+'\n') def inputs(): for line in sys.stdin: for word in line.split(): yield int(word) def todo(): for n in inputs(): for bits in 32,64: for i in range(len(impls[bits])): yield bits,n,i numresults = 0 numsuccesses = 0 firstfailure = None with open(f'{top}/overview','w') as f: with multiprocessing.Pool(os_threads) as pool: for bits,n,i,result in pool.imap(doit,todo(),chunksize=1): numresults += 1 if result == 'success': numsuccesses += 1 else: if firstfailure is None: firstfailure = f'{result}/int{bits}/impl{i}/{n}' f.write(f'{result}/int{bits}/impl{i}/{n} {impls[bits][i]}\n') f.flush() print(f'results: {numresults}') print(f'successes: {numsuccesses}') if numsuccesses != numresults: print(f'failures: {numresults-numsuccesses}; first is {firstfailure}')