hardware-counters
weak_sizes.py
#!/usr/bin/env python3
"""
Weak-scaling problem sizes: keep work PER RANK roughly constant so runtime
stays in a useful 10-60 s window as core count grows.
Motivation (see analysis/size_diag.py): with fixed global sizes on a 288-core
Cirrus node, miniFE was 90.5% sub-second, HPL 65%, CoMD 64%. Below ~1 s the
fixed overheads (MPI_Init, I/O, CrayPat instrumentation) dominate wall time and
the efficiency factor collapses, so the counters cannot explain runtime.
Scaling rules, by how each benchmark's size knob is defined:
miniFE nx=ny=nz is a GLOBAL 3D grid -> nx ~ base * cbrt(nranks)
CoMD -x -y -z is a GLOBAL lattice -> x ~ base * cbrt(nranks)
HPL N is the GLOBAL matrix order -> N ~ base * sqrt(nranks)
(work is O(N^3), memory O(N^2); sqrt keeps memory/rank constant)
STREAM array length is global -> len ~ base * nranks
HPCG nx/ny/nz are ALREADY per-rank -> keep fixed, just raise the base
LULESH -s is ALREADY per-rank -> keep fixed, just raise the base
usage: weak_sizes.py APP NRANK BASE
"""
import sys
app, nrank, base = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
n = max(nrank, 1)
if app in ("minife", "comd"):
v = int(round(base * n ** (1.0 / 3.0)))
v = max(8, (v // 4) * 4) # keep it a tidy multiple of 4
elif app == "hpl":
v = int(round(base * n ** 0.5))
v = max(1000, (v // 192) * 192) # multiple of the block size NB=192
elif app == "stream":
v = base * n
v = min(v, 400_000_000) # cap: memory per node
elif app in ("hpcg", "lulesh"):
v = base # already per-rank
else:
v = base
print(v)