Files
aare/python/tests/validation_tiers.py
T
kferjaoui 4c0a093e9f
Build on RHEL8 / build (push) Successful in 3m18s
Build on RHEL9 / build (push) Successful in 4m3s
Run tests using data on local RHEL8 / build (push) Successful in 4m10s
docs: Performance study
2026-08-21 10:04:08 +02:00

131 lines
4.9 KiB
Python

"""Tiered CPU/CUDA agreement study for the deck's validation slides.
Runs the three finders that differ in exactly one thing each over the same
frames, from the same pedestal, and scores every pair:
ClusterFinder serial CPU, pedestal pushed DURING the raster scan
ClusterFinderFrozen same logic, pedestal frozen per frame + deferred push
ClusterFinderCUDA frozen per frame, float32 device pedestal
so that serial vs frozen = update timing alone (a CPU-only effect)
and frozen vs cuda = everything CUDA changes.
The question the deck needs answered is not "how many disagree" but "in which
direction, and is a CUDA-only centre an invented photon or a second copy of one
both finders already found". So every CUDA-only centre is also scored at tol=1:
if it has a counterpart in the agreed set's 8-neighbourhood it is a duplicate,
not an invention.
Writes tiers.json + spectra_valid.png next to itself.
"""
import sys, json, time
sys.path.append('/home/ferjao_k/aare/build')
sys.path.append('/home/ferjao_k/aare/python/tests')
from pathlib import Path
import numpy as np
import boost_histogram as bh
import matplotlib
matplotlib.use("Agg")
from aare import File, ClusterFinder, ClusterFinderFrozen, ClusterFinderCUDA
from helper import centers, only_sets, shift_dist
OUT = Path(__file__).resolve().parent
BASE = Path('/mnt/sls_det_storage/moench_data/2603_MaxIVBeamtime/2026032408/'
'process/xrf/')
N_PED, N, N_SIGMA, N_STREAMS = 1000, 10000, 5, 4
CLUSTER = (3, 3)
IMG = (400, 400)
CAP = 50_000
NBINS, ERANGE = 200, (-2, 4000)
f = File(BASE / 'Cu_factor_10_data_master_0.json')
pd = File(BASE / 'Cu_factor_10_pedestal_master_0.json')
cf_cpu = ClusterFinder(IMG, CLUSTER, n_sigma=N_SIGMA, capacity=CAP)
cf_frz = ClusterFinderFrozen(IMG, CLUSTER, n_sigma=N_SIGMA, capacity=CAP)
cf_cud = ClusterFinderCUDA(IMG, CLUSTER, n_sigma=N_SIGMA,
max_clusters_per_frame=3000, n_streams=N_STREAMS)
finders = {'cpu': cf_cpu, 'frozen': cf_frz, 'cuda': cf_cud}
t0 = time.perf_counter()
pd.seek(0)
for _ in range(N_PED):
img = pd.read_frame().copy()
for cf in finders.values():
cf.push_pedestal_frame(img)
print(f'pedestal train: {time.perf_counter()-t0:.1f}s', flush=True)
f.seek(0)
data = f.read_n(N)
print('data:', data.shape, data.dtype, flush=True)
names = list(finders)
totals = {n: 0 for n in names}
hists = {n: bh.Histogram(bh.axis.Regular(NBINS, *ERANGE)) for n in names}
pairs = {(a, b): dict(a_only=0, b_only=0) for i, a in enumerate(names)
for b in names[i + 1:]}
# every CUDA-only centre, scored against the agreed set
extras = [] # one record per frozen-vs-cuda cuda-only centre
n_dup_tol1 = 0
t0 = time.perf_counter()
for fid in range(N):
cs = {}
for n, cf in finders.items():
cf.find_clusters(data[fid])
cv = cf.steal_clusters(realloc_same_capacity=True)
cs[n] = centers(cv)
totals[n] += len(cs[n])
if cv.size:
hists[n].fill(np.asarray(cv.sum()).ravel())
for (a, b), acc in pairs.items():
a_only, b_only = only_sets(cs[a], cs[b], tol=0)
acc['a_only'] += len(a_only)
acc['b_only'] += len(b_only)
# the tier that matters: frozen vs cuda, one record per extra
_, cu_only = only_sets(cs['frozen'], cs['cuda'], tol=0)
for p in cu_only:
d = shift_dist(p, cs['frozen'], R=4)
extras.append(dict(frame=int(fid), x=int(p[0]), y=int(p[1]),
shift=int(d)))
if cu_only:
_, cu_only_1 = only_sets(cs['frozen'], cs['cuda'], tol=1)
n_dup_tol1 += len(cu_only) - len(cu_only_1)
if fid % 1000 == 0:
print(f' {fid}/{N} {time.perf_counter()-t0:.0f}s', flush=True)
print(f'scan: {time.perf_counter()-t0:.0f}s', flush=True)
res = dict(n_frames=N, totals=totals,
pairs={f'{a} vs {b}': v for (a, b), v in pairs.items()},
extras=extras,
n_cuda_only=len(extras),
n_adjacent_to_agreed=n_dup_tol1,
shift_histogram={str(k): int(v) for k, v in
zip(*np.unique([e['shift'] for e in extras],
return_counts=True))} if extras else {},
hists={n: h.values().tolist() for n, h in hists.items()},
edges=hists[names[0]].axes[0].edges.tolist())
(OUT / 'tiers.json').write_text(json.dumps(res))
print('\n=== totals ===')
for n in names:
print(f' {n:8s} {totals[n]:>12,}')
print('\n=== pairwise (tol=0) ===')
for (a, b), v in pairs.items():
tot = v['a_only'] + v['b_only']
print(f' {a:>6s} vs {b:<6s} {a}-only {v["a_only"]:>4} '
f'{b}-only {v["b_only"]:>4} total {tot:>4} '
f'({tot/max(totals[a],1):.2e})')
print('\n=== the frozen-vs-cuda extras ===')
print(f' cuda-only centres (tol=0): {len(extras)}')
print(f' of which adjacent to an agreed centre (tol=1): {n_dup_tol1}')
print(f' chebyshev shift to nearest frozen centre: {res["shift_histogram"]}')