From a3c2ac1572b82fb802e6d74ec083cc533c9c56e9 Mon Sep 17 00:00:00 2001 From: Ferjaoui Date: Thu, 29 Oct 2026 16:44:15 +0100 Subject: [PATCH] perf: range-scale and pedestal-centring options in the hist probe For the cache-footprint experiments: --range-scale widens the bins without changing n_bins, and --center subtracts each pixel's median so neighbouring pixels share bins, while u16 frames keep their H2D size. --- python/tests/perf/hist/nsys_hist_probe.py | 45 ++++++++++++++++++++--- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/python/tests/perf/hist/nsys_hist_probe.py b/python/tests/perf/hist/nsys_hist_probe.py index ddaa0ab0..e9d71a86 100644 --- a/python/tests/perf/hist/nsys_hist_probe.py +++ b/python/tests/perf/hist/nsys_hist_probe.py @@ -1,10 +1,21 @@ """The process nsys traces: fill one histogram with N frames, read it out, exit. nsys_hist_probe.py + [--range-scale S] [--center OFFSET] dtype is u16, f32 or f64 (the frame dtype copied to the GPU). Run by -run_probes.py; run directly only under `nsys profile --trace=cuda`. The wall -times printed are profiler-inflated and are not throughput numbers. +run_probes.py; run directly only under `nsys profile --trace=cuda` or ncu. The +wall times printed are profiler-inflated and are not throughput numbers. + +The range is taken from the data. Two options change what the kernel sees, +for the cache-footprint experiments: + + --range-scale S widen the range S times around its centre, keeping n_bins: + bins S times wider, accumulator the same size. + --center OFFSET subtract each pixel's median (first 200 frames) and add + OFFSET, rounded and clipped to uint16. All pixels then sit + near OFFSET, so neighbouring pixels share bins, while u16 + frames keep the same H2D bytes. Only `chunk` frames are read and held in host memory. They are registered once and passed to fill_histogram() repeatedly until n_frames have been queued: the @@ -12,6 +23,7 @@ histogram doesn't depend on frame order, and every pass is a real H2D copy. The chunk is rounded down to a multiple of the batch size so that no pass ends in a partial batch. """ +import argparse import math import sys import time @@ -27,15 +39,34 @@ from aare import File, PixelHistogramCUDA # noqa: E402 DTYPES = {"u16": np.uint16, "f32": np.float32, "f64": np.float64} -dtype = DTYPES[sys.argv[1]] -N, n_bins, batch, chunk = (int(a) for a in sys.argv[2:6]) -chunk = max(batch, min(chunk, N) // batch * batch) +ap = argparse.ArgumentParser() +ap.add_argument("dtype", choices=DTYPES) +ap.add_argument("n_frames", type=int) +ap.add_argument("n_bins", type=int) +ap.add_argument("batch", type=int) +ap.add_argument("chunk", type=int) +ap.add_argument("--range-scale", type=float, default=1.0) +ap.add_argument("--center", type=float, default=None) +args = ap.parse_args() + +dtype = DTYPES[args.dtype] +N, n_bins, batch = args.n_frames, args.n_bins, args.batch +chunk = max(batch, min(args.chunk, N) // batch * batch) f = File(common.DATA_FILE) data = f.read_n(chunk) +if args.center is not None: + # Median, not mean: photon hits pull a pixel's mean up by a different + # amount in each pixel, which would leave neighbours in different bins. + ped = np.median(data[:min(chunk, 200)], axis=0).astype(np.float32) + data = np.clip(np.rint(data.astype(np.float32) - ped + np.float32(args.center)), + 0, np.iinfo(np.uint16).max) # Range from the data itself, so that nearly every value is histogrammed. lo, hi = np.percentile(data[:min(chunk, 200)], [0.1, 99.9]) xmin, xmax = float(math.floor(lo)), float(math.ceil(hi) + 1) +if args.range_scale != 1.0: + mid, half = (xmin + xmax) / 2, (xmax - xmin) / 2 * args.range_scale + xmin, xmax = float(math.floor(mid - half)), float(math.ceil(mid + half)) data = np.ascontiguousarray(data.astype(dtype, copy=False)) rows, cols = data.shape[1:] @@ -63,8 +94,10 @@ hist.unregister_output_buffer() in_range = out.sum(dtype=np.uint64) / (N * rows * cols) print(f"dtype={data.dtype} N={N} n_bins={n_bins} batch={batch} chunk={chunk} " - f"range=[{xmin:g}, {xmax:g})") + f"range=[{xmin:g}, {xmax:g}) bin width={(xmax - xmin) / n_bins:.2f}" + f"{' centred' if args.center is not None else ''}") print(f" H2D/frame={rows * cols * data.itemsize:,} B " f"readout={out.nbytes:,} B in range={100 * in_range:.2f}%") print(f" wall fill={t_fill:.3f}s ({N / t_fill:.0f} FPS) readout={t_read * 1e3:.1f} ms " f"(profiler-inflated)") +