diff --git a/tools/battery/results/timing_platforms/platforms.json b/tools/battery/results/timing_platforms/platforms.json new file mode 100644 index 000000000..a5001e8e2 --- /dev/null +++ b/tools/battery/results/timing_platforms/platforms.json @@ -0,0 +1,289 @@ +{ + "title": "rugnux timing benchmark across platforms: rc175 3be5dcaac", + "intro": "`battery.py timing` (tools/battery/README.md, 'The timing benchmark'; timing.py, timing.json). myob_x10sa and cytc_x10sa start from AlphaFold DB models, 4nwv from its deposited model moved by a fixed transform; each must be placed by molecular replacement. Code: rc175 3be5dcaac, pushed 2026-10-16; its rugnux code is identical to e25876055 (the later commits change docs only). Each platform's numbers come from its own pack (named under Reproducibility); 'not recorded' means the pack does not have it. This file is generated by timing_table.py from platforms.json, kept beside it with a copy of timing.json: add a platform to platforms.json and re-run `python3 -I timing_table.py platforms.json -o `.", + "timing_json": "../../timing.json", + "stands_out": [ + "Every check passed on every platform and run (server and GB10 included) (6 of 6 checks on each of the 3 sets). The MR decisions are the same everywhere: myob and 4nwv AGREES. For cytc the data give P 31 2 1 and MR places the model in P 32 2 1 (ENANTIOMORPH); the Mac report gives the same hand but no MR_DECISION key.", + "The steps cover rugnux's whole clock on every platform: on 4nwv the sum of the TIMING steps equals WALL_TIME to 0.1 s. Outside that clock, process launch to exit (elapsed_s) adds 0.5-1.0 s. There is no hidden time between steps.", + "On Windows, the first set of a run (myob_x10sa) has 4.0-4.3 s between process launch and exit that rugnux's own clock does not cover; the later sets and every other platform have 0.1-1.0 s. What that time is (process or CUDA start-up before step 1, say) is not recorded.", + "Workstation (RTX 5080): 4nwv takes 84 s. Scaling and merging is the biggest step (25 s), then MR (20 s), then integration (10.5 s). The run of 10-15 23:26, said to be on a loaded machine, is within 1% of the clean run. So load did not inflate that particular run, whatever it did to others.", + "Workstation (RTX 5080): 4nwv takes 84 s. Scale & merge (P1 merge, symmetry, final merge, analyses) is the largest stage at 28 s. Structure & map follows at 27 s (MR 20 s of it), then post-refine at 8 s and the two per-image passes at 10.5 s together. The run of 10-15 23:26, said to be on a loaded machine, is within 1% of the clean run, and used the identical binary. So load did not inflate that particular run, whatever it did to others.", + "Workstation CPU only: 4nwv takes 515 s, 6.2x the GPU run. Structure & map takes 331 s of it (MR 243 s, about 12x the GPU), and the per-image passes 83 s (8x). Scale & merge is only 2x slower (56 s). The two small sets are 7x slower.", + "Mac M6 (CPU + Metal integration): 4nwv takes 431 s, 0.84x the workstation's CPU-only time. Its MR runs on the CPU and takes as long as the workstation's CPU-only MR (240 vs 243 s); it is 56% of the Mac's wall time. Metal brings the per-image passes to 55 s (CPU-only workstation: 83 s). Model validation (56 s) and scale & merge (49 s) also beat the 16-core CPU-only run. Caveat: the Mac binary has no -march flags, the workstation's has x86-64-v3.", + "Windows laptop (RTX PRO 2000, 8 GB): 4nwv takes 167-206 s, 2.0-2.5x the workstation GPU run. Every stage is slower: per-image passes 27 s (2.6x), scale & merge 40-58 s, structure & map 71-87 s (MR 52-62 s, 2.7-3.1x). Back-to-back runs differ by 23%, spread over the GPU/CPU-heavy stages. The laptop also spends 2.6-5.6 s writing files and 3.3-4.4 s in the geometry-walk probe (0.5 and 0.1 s on the workstation).", + "GB10 (DGX Spark, unified memory): 4nwv takes 123 s, 1.47x the RTX 5080 workstation and faster than the laptop (167-206 s). By stage against the workstation: scale & merge is nearly level (29.0 vs 27.9 s), the per-image passes are 1.4x (15.1 vs 10.5 s), and structure & map is 2.3x (61.0 vs 27.1 s; MR alone 2.4x, 46.6 vs 19.7 s). MR is its largest step, 38% of the wall time; per the pack's profile the GPU rotation function alone takes 25.7 s. The small sets lose more: myob 17.1 s and cytc 25.7 s, 1.9x the workstation. Everything ran on the GPU. With a 104.5 GB budget, model validation planned 22.1 GB with 10 rigid-body engines and MR 21.6 GB with 9; the 8 and 16 GB cards ran 1-2.", + "Server (4x Xeon Gold 6146, 4x T4; rugnux uses the 24 CPUs of the GPUs' sockets and all four cards): 4nwv takes 248 s. That is 3.0x the RTX 5080 workstation and slower than the 8 GB laptop (167-206 s). The gap is almost all structure & map: 150 s, of which MR is 123 s (6.3x the workstation's 19.7 s, 2.0-2.4x the laptop's) and model validation 26 s (3.5x). Scale & merge (57 s) and post-refine (16 s) are about 2x the workstation. The two per-image passes, spread over four cards, are level with it (11.0 vs 10.5 s). The small sets match GB10: myob 16.9 s and cytc 25.8 s (GB10: 17.1 and 25.7 s), 1.8-2.0x the workstation, with pre-scan and indexing the largest stages. The run started 5 s after a full battery on the same machine ended; the load during it was not recorded.", + "Peak host memory for 4nwv: workstation GPU 18.4 GiB RSS, workstation CPU-only 26.8 GiB, Mac 9.1 GiB RSS (~22 GiB physical footprint). The Windows, GB10 and server packs recorded none. The two numbers are not directly comparable: Mac RSS vs footprint, unified memory." + ], + "platforms": [ + { + "platform": "Workstation: AMD Ryzen 9 5950X (16C/32T)", + "gpu": "RTX 5080 16 GB", + "role": "reference (clean)", + "run": "/data/battery/runs/20261016-0941_e25876_ws-3be5dc-timing", + "os": "Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs)", + "build": "GCC 13.3.1 (gcc-toolset-13), nvcc 13.0.88, CUDA code for sm_75/80/86/89/90/100/120/121 (the default list; JFJOCH_CUDA_ARCHITECTURES empty), CMAKE_CXX_FLAGS/CMAKE_C_FLAGS = -march=x86-64-v3, no LTO; build dir /data/tmp/rc175-metal-merge/build (CMakeCache and CMakeFiles read 2026-10-16)", + "battery_cmd": "`cd /home/jungfrau/nextgendcu && nice -n 0 python3 tools/battery/battery.py --site /data/battery/site.json timing --rugnux /data/tmp/rc175-metal-merge/build/rugnux/rugnux --gpulock --label ws-3be5dc-timing`", + "conditions": "started 09:41:50 right after a full battery ended (09:41); no other rugnux/battery/build process, GPU lock free, 0.3 GiB in use on the card before; load average 1.43 before, 6.70 after, highest 1-min load sampled during the run 9.75 (rugnux's own threads); page cache warm (the full battery had just read the same datasets); not the first run after the build", + "peak_mem": { + "myob_x10sa": "3.5 GiB RSS", + "cytc_x10sa": "5.1 GiB RSS", + "4nwv": "18.4 GiB RSS" + }, + "notes": [ + "Peak RSS = VmHWM from /proc//status, sampled every 0.5 s. Whole-card GPU memory in use peaked at 8.1 GiB (nvidia-smi every 0.5 s; 0.3 GiB idle baseline).", + "The binary reports git e25876; the rugnux code of e25876055 and 3be5dcaac is identical (the later commits change docs only). The battery runner was the shared checkout at 2be738f55 (3be5dcaac plus the Windows battery fixes).", + "The manifest's 'source HEAD' is the build directory's source tree at run time (it had moved on to 2be738f55); the commit the binary was built from is the one it reports (e25876)." + ] + }, + { + "platform": "Workstation: AMD Ryzen 9 5950X (16C/32T)", + "gpu": "none (--no-gpu)", + "role": "reference (clean)", + "run": "/data/battery/runs/20261016-0943_e25876_ws-3be5dc-timing-nogpu", + "os": "Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs)", + "build": "GCC 13.3.1 (gcc-toolset-13), nvcc 13.0.88, CUDA code for sm_75/80/86/89/90/100/120/121 (the default list; JFJOCH_CUDA_ARCHITECTURES empty), CMAKE_CXX_FLAGS/CMAKE_C_FLAGS = -march=x86-64-v3, no LTO; build dir /data/tmp/rc175-metal-merge/build (CMakeCache and CMakeFiles read 2026-10-16) (same binary as the GPU run)", + "battery_cmd": "`cd /home/jungfrau/nextgendcu && nice -n 0 python3 tools/battery/battery.py --site /data/battery/site.json timing --rugnux /data/tmp/rc175-metal-merge/build/rugnux/rugnux --gpulock --extra=--no-gpu --label ws-3be5dc-timing-nogpu`", + "conditions": "started 09:43:50 right after the GPU run, nothing else running; load average 5.67 before, 12.22 after, highest 1-min load sampled 34.1 during 4nwv (rugnux's own 32 threads); page cache warm", + "peak_mem": { + "myob_x10sa": "9.9 GiB RSS", + "cytc_x10sa": "10.0 GiB RSS", + "4nwv": "26.8 GiB RSS" + }, + "notes": [ + "Peak RSS = VmHWM from /proc, sampled every 0.5 s.", + "Runner and binary as in the GPU run.", + "The manifest's 'source HEAD' is the build directory's source tree at run time (it had moved on to 2be738f55); the commit the binary was built from is the one it reports (e25876)." + ] + }, + { + "platform": "Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24)", + "gpu": "4x Tesla T4 (15360 MiB each, as recorded)", + "role": "reference", + "archive": "/data/perf/bundle_rc175_3be5dc.tar.gz", + "member": "bundle_rc175_3be5dc/b_mr_timing", + "os": "not recorded", + "build": "Release, -march=x86-64-v3, CUDA on (manifest; build dir build_v3); compiler, CUDA version, CUDA arch and LTO not recorded", + "battery_cmd": "not recorded (the pack's console.log shows `battery.py timing` output; the manifest: label 'timing', no gpulock, no extra arguments)", + "conditions": "started 05:31:22, 5 s after a full battery run on the same machine ended (03:27:52 to 05:31:17, the pack's a_battery), so the page cache was presumably warm (not recorded as such); GPU not shared during that battery (its report); load average at 05:37:04, 40 s after the timing run ended: 0.52 / 3.85 / 6.32 (1 / 5 / 15 min), all four GPUs idle then (c_profile machine_before); load during the run not recorded; rugnux limits itself to 24 threads, the CPUs of the GPUs' sockets (24-47), and uses all 4 GPUs", + "notes": [ + "Peak memory not recorded. 4nwv model validation planned 5.18 GB of a 12.52 GB budget (80% of the card's 15.65 GB) and MR 4.79 GB, on the GPU.", + "The pack also holds the full battery (a_battery) and nsys/perf profiles (c_profile); they are not used here." + ], + "run_name": "20261016-0531_3be5dc_timing (on the server; b_mr_timing in the pack, name from its console.log)" + }, + { + "platform": "DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded)", + "gpu": "GB10 (unified memory, 121.7 GiB recorded)", + "role": "reference", + "archive": "/data/perf/rugnux_gb10_perf_rc175-3be5dc_20261016.tar.gz", + "member": "rugnux_gb10_perf_rc175-3be5dc_20261016/battery/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr", + "os": "not recorded (DGX Spark per the pack's ANALYSIS.md)", + "build": "Release, -DJFJOCH_RUGNUX_ONLY=ON -DJFJOCH_CUDA_ARCHITECTURES=121, no -march (ANALYSIS.md); LTO, compiler and CUDA versions not recorded", + "battery_cmd": "not recorded (ANALYSIS.md: rc175's tools/battery `battery.py timing`, the pack's data, an absolute-path site config)", + "conditions": "GPU not shared: the resident GPU service was stopped for the whole session (ANALYSIS.md; the report says 'GPU was not shared during the run'); no gpulock; started 4 min after a six-set battery run on the same data (page cache state not recorded); load not recorded; the pack's pdb_cache JSONs were repointed by hand before this run", + "notes": [ + "Peak memory not recorded. With unified memory the GPU budget is 104.53 GB: 4nwv model validation planned 22.12 GB (10 rigid-body engines), MR 21.60 GB (9 rigid-body engines).", + "The same binary with the deposited 4nwv model (six-set run 20261016-0940_3be5dc_gb10-rc175-3be5dc in the same pack): 76.7 s." + ] + }, + { + "platform": "Laptop: Intel Core Ultra 7 255H (16C/16T)", + "gpu": "RTX PRO 2000 Blackwell Laptop 8 GB", + "role": "reference", + "archive": "/data/perf/rugnux_win11_perf_3be5dc_20261016.tar.gz", + "member": "rugnux_win11_perf_3be5dc_20261016/runs/1_timing", + "os": "Windows 11 (pack name; build number not recorded); GPU in WDDM mode (BRIEF.md)", + "build": "MSVC 19.51 + CUDA 13.3, CI Windows settings /arch:AVX (BRIEF.md); CUDA arch and LTO not recorded; build dir build-win-ci", + "battery_cmd": "not recorded (BRIEF.md: `battery.py timing` with the runner patched by battery-windows.patch, run from the pack's unmodified site.json)", + "conditions": "nothing else on the GPU, no gpulock (site gpulock null); the session's second timing run (the first is the next row); load and page cache not recorded", + "notes": [ + "Why this run is the reference: it is the run the pack's BRIEF.md reports as its result (`1_timing`, the patched runner from the pack's own site.json). The earlier run (next row) used the same binary (same sha256) and the unpatched runner. BRIEF.md puts the 167 vs 206 s 4nwv spread down to laptop thermals/power; that is its reading, not a measurement." + ], + "run_name": "20261016-0753_3be5dc_3be5dc-timing-patched (on the laptop; runs/1_timing in the pack, name from its SOURCE_RUN.txt)" + }, + { + "platform": "Laptop: Intel Core Ultra 7 255H (16C/16T)", + "gpu": "RTX PRO 2000 Blackwell Laptop 8 GB", + "role": "repeat (earlier run)", + "archive": "/data/perf/rugnux_win11_perf_3be5dc_20261016.tar.gz", + "member": "rugnux_win11_perf_3be5dc_20261016/runs/repeat_timing", + "os": "Windows 11 (pack name; build number not recorded)", + "build": "same binary as the reference row (sha256 ce0248ef...)", + "battery_cmd": "not recorded (BRIEF.md: the repo's unpatched runner with an absolute-path site config)", + "conditions": "the session's first timing run; nothing else on the GPU; load and page cache not recorded; hardware not recorded in this run's manifest (same host PC17975)", + "run_name": "20261016-0741_3be5dc_3be5dc-timing (on the laptop; runs/repeat_timing in the pack, name from its SOURCE_RUN.txt)" + }, + { + "platform": "Mac mini: Apple M6, 12 cores", + "gpu": "Apple M6 GPU (Metal, integration only)", + "role": "reference", + "conditions": "nothing else running; data read cold (the report)", + "devices": [ + "CPU", + "CPU", + "CPU (no CUDA build)" + ], + "peak_mem": { + "myob_x10sa": "6.7 GiB RSS (~6.9 GiB footprint)", + "cytc_x10sa": "5.9 GiB RSS (~10 GiB footprint)", + "4nwv": "9.1 GiB RSS (~22 GiB footprint)" + }, + "notes": [ + "Peak memory sampled once a second by the Mac agent (ps RSS; top's physical footprint, rounded to whole GiB above 10 GiB).", + "The pack's 4nwv pdb_cache JSON had to be corrected by hand before 4nwv would run." + ], + "manual": { + "sets": { + "myob_x10sa": { + "verdict": "pass", + "wall": 40.4, + "steps": { + "Molecular replacement": 1.6 + } + }, + "cytc_x10sa": { + "verdict": "pass", + "wall": 56.8, + "steps": { + "Molecular replacement": 4.3 + } + }, + "4nwv": { + "verdict": "pass", + "wall": 430.6, + "steps": { + "Pre-scan": 3.9, + "Indexing": 10.1, + "Integration (geometry pre-pass)": 24.9, + "Scaling and post-refinement": 9.4, + "Metric-symmetry check": 5.3, + "Integration (refined geometry)": 30.1, + "Scaling and merging": 45.6, + "Analyses": 3.2, + "Model validation": 56.4, + "Molecular replacement": 239.9, + "Writing files": 1.7 + } + } + }, + "repro": { + "run directory": "runs/20261016-0727_3be5dc_timing-3be5dc (on the Mac)", + "date/time": "2026-10-16 07:27 start (from the run directory name); end not recorded", + "host": null, + "OS": "macOS (version not recorded)", + "rugnux (as the binary reports)": "not recorded (the report: origin/rc175 tip 3be5dcaac, metal-integration merged)", + "build flags (as the binary reports)": null, + "build (battery manifest)": "not recorded (the report: Release, no -march/LTO flags, JFJOCH_USE_CUDA=OFF, built in the jungfraujoch-origin-rc175 worktree)", + "compiler / CUDA / arch": "not recorded (no CUDA; Metal integration engine)", + "battery command": "not recorded (the report: `battery.py timing` with an absolute-path copy of the site config)", + "battery runner": "the repo's tools/battery at 3be5dcaac (the report), not patched", + "threads (rugnux reports)": null, + "hardware (manifest)": "Mac mini M6, 12 cores (performance/efficiency split not recorded), 16 GB (the report)", + "GPU (rugnux reports)": null, + "rugnux command, myob_x10sa": null, + "rugnux command, cytc_x10sa": null, + "rugnux command, 4nwv": null + } + }, + "table_notes": { + "4nwv": "the Mac pack is one markdown report (rugnux_mac_timing_3be5dc_20261016.md), no run directory or logs. Its 4nwv step table lists 11 steps; Reading input, Indexing at the refined geometry and Geometry check are not in it (n/r), so its sum covers only the listed steps; elapsed_s is not in it.", + "cytc_x10sa": "the Mac report gives only the wall time and the MR step (4.3 s) for this set; no other step is in it (n/r).", + "myob_x10sa": "the Mac report gives only the wall time and the MR step (1.6 s) for this set; no other step is in it (n/r)." + } + }, + { + "platform": "Workstation: AMD Ryzen 9 5950X (16C/32T)", + "gpu": "RTX 5080 16 GB", + "role": "earlier run (10-15 23:26)", + "run": "/data/battery/runs/20261015-2326_e25876_rc175-r10-timing", + "os": "Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs)", + "build": "same binary as the clean workstation runs (sha256 identical)", + "battery_cmd": "not recorded (options in the manifest below)", + "conditions": "described by the owner as taken while the machine was heavily loaded by other agents (load 15-35); load during this run not recorded; its times are within 1% of the clean run", + "notes": [ + "Same binary as the clean runs: identical sha256 (4ace4e1f...); at this run the source tree was still at e25876055." + ] + } + ], + "step_groups": [ + { + "box": "Input", + "steps": [ + "Reading input" + ], + "why": "opening the dataset and reading its header" + }, + { + "box": "Pre-scan", + "steps": [ + "Pre-scan" + ], + "why": "frame projection, spot widths, beam-stop shadow, defective pixels" + }, + { + "box": "Index", + "steps": [ + "Indexing", + "Metric-symmetry check", + "Indexing at the refined geometry" + ], + "why": "pass 1 first-pass indexing; the metric-symmetry check (only where the free refinement departs from the promoted Bravais class: the alternative class is re-indexed and re-run as a probe on a copy, RugnuxPasses.cpp); pass 2's re-indexing at the post-refined geometry. Pass 1 and pass 2 are kept together because pass 2's indexing is 0.1-0.2 s" + }, + { + "box": "Per image, pass 1", + "steps": [ + "Integration (geometry pre-pass)" + ], + "why": "the per-image chain (decode, azimuthal profile, spot finding, refine, predict, integrate) at the header geometry" + }, + { + "box": "Post-refine", + "steps": [ + "Scaling and post-refinement", + "Geometry check" + ], + "why": "pass 1's P1 scale-and-merge plus the post-refinement of distance, beam, cell and axis (one Step in RugnuxScaleMerge.cpp); and the geometry walk after pass 2, which re-indexes a probe at the refined geometry to check it holds (RugnuxPasses.cpp; it is logged last, after Writing files)" + }, + { + "box": "Per image, pass 2", + "steps": [ + "Integration (refined geometry)", + "Integration" + ], + "why": "the per-image chain again: 'Integration (refined geometry)' where post-refinement moved the detector geometry, plain 'Integration' where it did not and pass 2 runs at the header geometry (RugnuxImagePass.cpp; cytc_x10sa here)" + }, + { + "box": "Scale & merge (P1) -> Symmetry -> Scale & merge", + "steps": [ + "Scaling and merging", + "Analyses" + ], + "why": "one Step covers the P1 merge, the space-group search and the merge in the chosen group (plus pass 2's post-refinement observations and the rotation-scale probe); Analyses is the anisotropy, tNCS and twinning analysis of that merge. Kept as one column: the sub-timings inside the step overlap (the all-observation P1 merge runs concurrently with the space-group search, and 'space-group search' encloses 'merge '), so the three boxes cannot be split exactly" + }, + { + "box": "Structure & map", + "steps": [ + "Model validation", + "Molecular replacement" + ], + "why": "Model validation is the rigid-body fit of the given model against its null; on 4nwv the moved model is rejected there. For the AlphaFold predictions (myob, cytc) there is no Model validation step, because a predicted model goes straight to MR (RugnuxScaleMerge.cpp). Molecular replacement covers the search, the placement and the maps" + }, + { + "box": "Report", + "steps": [ + "Writing files" + ], + "why": "MTZ, mmCIF, HKL and maps" + } + ], + "step_extra_columns": [ + [ + "of which Molecular replacement", + "Molecular replacement" + ] + ], + "step_notes": [ + "The figure's 'Geometry' box is the stills workflow's and does not apply here." + ] +} diff --git a/tools/battery/results/timing_platforms/rugnux_timing_platforms_3be5dc.md b/tools/battery/results/timing_platforms/rugnux_timing_platforms_3be5dc.md new file mode 100644 index 000000000..b4d7a01c5 --- /dev/null +++ b/tools/battery/results/timing_platforms/rugnux_timing_platforms_3be5dc.md @@ -0,0 +1,315 @@ +# rugnux timing benchmark across platforms: rc175 3be5dcaac + +`battery.py timing` (tools/battery/README.md, 'The timing benchmark'; timing.py, timing.json). myob_x10sa and cytc_x10sa start from AlphaFold DB models, 4nwv from its deposited model moved by a fixed transform; each must be placed by molecular replacement. Code: rc175 3be5dcaac, pushed 2026-10-16; its rugnux code is identical to e25876055 (the later commits change docs only). Each platform's numbers come from its own pack (named under Reproducibility); 'not recorded' means the pack does not have it. This file is generated by timing_table.py from platforms.json, kept beside it with a copy of timing.json: add a platform to platforms.json and re-run `python3 -I timing_table.py platforms.json -o `. + +## Table 1: wall time (s), timing benchmark + +Wall = rugnux's own WALL_TIME. Checks = the benchmark's six checks on each set (space_group, placed, mr_group, r_free, confirmed, placement_cc); a failing check would be named. + +| Platform | GPU | run | myob_x10sa | cytc_x10sa | 4nwv | checks | MR decision (myob / cytc / 4nwv) | +|:--|:--|:--|--:|--:|--:|:--|:--| +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) | 9.2 | 13.2 | 83.7 | all pass | AGREES / ENANTIOMORPH / AGREES | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) | 65.4 | 95.7 | 515.1 | all pass | AGREES / ENANTIOMORPH / AGREES | +| Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference | 16.9 | 25.8 | 247.7 | all pass | AGREES / ENANTIOMORPH / AGREES | +| DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference | 17.1 | 25.7 | 123.2 | all pass | AGREES / ENANTIOMORPH / AGREES | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference | 23.4 | 35.0 | 205.7 | all pass | AGREES / ENANTIOMORPH / AGREES | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) | 23.3 | 36.0 | 167.2 | all pass | AGREES / ENANTIOMORPH / AGREES | +| Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference | 40.4 | 56.8 | 430.6 | all pass | not recorded / not recorded / not recorded | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) | 9.2 | 13.4 | 84.5 | all pass | AGREES / ENANTIOMORPH / AGREES | + +## Table 2: 4nwv, time per pipeline stage (s) + +rugnux's `TIMING step` lines of 4nwv's p_log.txt, grouped into the boxes of the workflow figure (docs/images/rugnux_pipeline.svg); the mapping is below. `-` = none of the stage's steps ran (from the log); `n/r` = a pack without logs reports none of them; `*` = it reports only some of them, so a sum marked `*` is partial. Wall - sum shows whether the steps cover rugnux's own clock (WALL_TIME). Elapsed (the battery's `elapsed_s`, from process launch to exit) minus wall is the time outside rugnux's clock: process start-up before step 1 and the exit after the last step. + +| Platform | GPU | run | Input | Pre-scan | Index | Per image, pass 1 | Post-refine | Per image, pass 2 | Scale & merge (P1) -> Symmetry -> Scale & merge | Structure & map | Report | of which Molecular replacement | **sum of steps** | **wall (WALL_TIME)** | wall - sum | elapsed (elapsed_s) | elapsed - wall | +|:--|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) | 0.1 | 4.2 | 5.0 | 5.5 | 8.3 | 5.0 | 27.9 | 27.1 | 0.5 | 19.7 | 83.7 | 83.7 | +0.0 | 84.2 | +0.5 | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) | 0.1 | 7.0 | 23.2 | 36.8 | 15.0 | 46.2 | 55.4 | 330.7 | 0.5 | 243.0 | 515.1 | 515.1 | -0.0 | 515.7 | +0.6 | +| Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference | 0.2 | 6.2 | 6.5 | 4.8 | 16.2 | 6.2 | 56.9 | 149.6 | 1.1 | 123.4 | 247.7 | 247.7 | +0.0 | 249.3 | +1.6 | +| DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference | 0.2 | 3.8 | 4.2 | 6.5 | 9.5 | 8.6 | 29.0 | 61.0 | 0.6 | 46.6 | 123.3 | 123.2 | -0.0 | 124.2 | +1.0 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference | 0.2 | 4.2 | 8.3 | 11.8 | 14.9 | 15.4 | 58.1 | 87.2 | 5.6 | 61.5 | 205.7 | 205.7 | -0.0 | 206.6 | +0.9 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) | 0.2 | 4.6 | 8.4 | 11.8 | 13.6 | 15.5 | 39.6 | 70.9 | 2.6 | 52.3 | 167.2 | 167.2 | +0.0 | 168.0 | +0.8 | +| Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference | n/r | 3.9 | 15.4* | 24.9 | 9.4* | 30.1 | 48.8 | 296.3 | 1.7 | 239.9 | 430.5* | 430.6 | +0.1* | not recorded | not recorded | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) | 0.1 | 4.4 | 5.0 | 5.3 | 8.5 | 5.1 | 28.7 | 27.0 | 0.6 | 19.5 | 84.5 | 84.5 | -0.0 | 85.0 | +0.5 | + +Figure box <- rugnux steps (what each step covers, from rugnux/*.cpp): + +- **Input** <- Reading input: opening the dataset and reading its header +- **Pre-scan** <- Pre-scan: frame projection, spot widths, beam-stop shadow, defective pixels +- **Index** <- Indexing + Metric-symmetry check + Indexing at the refined geometry: pass 1 first-pass indexing; the metric-symmetry check (only where the free refinement departs from the promoted Bravais class: the alternative class is re-indexed and re-run as a probe on a copy, RugnuxPasses.cpp); pass 2's re-indexing at the post-refined geometry. Pass 1 and pass 2 are kept together because pass 2's indexing is 0.1-0.2 s +- **Per image, pass 1** <- Integration (geometry pre-pass): the per-image chain (decode, azimuthal profile, spot finding, refine, predict, integrate) at the header geometry +- **Post-refine** <- Scaling and post-refinement + Geometry check: pass 1's P1 scale-and-merge plus the post-refinement of distance, beam, cell and axis (one Step in RugnuxScaleMerge.cpp); and the geometry walk after pass 2, which re-indexes a probe at the refined geometry to check it holds (RugnuxPasses.cpp; it is logged last, after Writing files) +- **Per image, pass 2** <- Integration (refined geometry) + Integration: the per-image chain again: 'Integration (refined geometry)' where post-refinement moved the detector geometry, plain 'Integration' where it did not and pass 2 runs at the header geometry (RugnuxImagePass.cpp; cytc_x10sa here) +- **Scale & merge (P1) -> Symmetry -> Scale & merge** <- Scaling and merging + Analyses: one Step covers the P1 merge, the space-group search and the merge in the chosen group (plus pass 2's post-refinement observations and the rotation-scale probe); Analyses is the anisotropy, tNCS and twinning analysis of that merge. Kept as one column: the sub-timings inside the step overlap (the all-observation P1 merge runs concurrently with the space-group search, and 'space-group search' encloses 'merge '), so the three boxes cannot be split exactly +- **Structure & map** <- Model validation + Molecular replacement: Model validation is the rigid-body fit of the given model against its null; on 4nwv the moved model is rejected there. For the AlphaFold predictions (myob, cytc) there is no Model validation step, because a predicted model goes straight to MR (RugnuxScaleMerge.cpp). Molecular replacement covers the search, the placement and the maps +- **Report** <- Writing files: MTZ, mmCIF, HKL and maps +- The figure's 'Geometry' box is the stills workflow's and does not apply here. + +- Mac mini: Apple M6, 12 cores (Apple M6 GPU (Metal, integration only)): the Mac pack is one markdown report (rugnux_mac_timing_3be5dc_20261016.md), no run directory or logs. Its 4nwv step table lists 11 steps; Reading input, Indexing at the refined geometry and Geometry check are not in it (n/r), so its sum covers only the listed steps; elapsed_s is not in it. + +## Table 3: cytochrome C (cytc_x10sa), time per pipeline stage (s) + +rugnux's `TIMING step` lines of cytc_x10sa's p_log.txt, grouped into the boxes of the workflow figure (docs/images/rugnux_pipeline.svg); the mapping is under Table 2. `-` = none of the stage's steps ran (from the log); `n/r` = a pack without logs reports none of them; `*` = it reports only some of them, so a sum marked `*` is partial. Wall - sum shows whether the steps cover rugnux's own clock (WALL_TIME). Elapsed (the battery's `elapsed_s`, from process launch to exit) minus wall is the time outside rugnux's clock: process start-up before step 1 and the exit after the last step. + +| Platform | GPU | run | Input | Pre-scan | Index | Per image, pass 1 | Post-refine | Per image, pass 2 | Scale & merge (P1) -> Symmetry -> Scale & merge | Structure & map | Report | of which Molecular replacement | **sum of steps** | **wall (WALL_TIME)** | wall - sum | elapsed (elapsed_s) | elapsed - wall | +|:--|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) | 0.2 | 1.1 | 1.4 | 1.3 | 1.0 | 3.3 | 3.6 | 1.1 | 0.2 | 1.1 | 13.2 | 13.2 | -0.0 | 13.5 | +0.3 | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) | 0.2 | 5.8 | 10.3 | 17.8 | 1.5 | 46.0 | 7.2 | 6.8 | 0.1 | 6.8 | 95.7 | 95.7 | +0.0 | 96.0 | +0.3 | +| Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference | 0.2 | 3.2 | 3.2 | 1.7 | 2.2 | 4.7 | 7.7 | 2.4 | 0.4 | 2.4 | 25.7 | 25.8 | +0.0 | 26.7 | +0.9 | +| DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference | 0.2 | 2.1 | 2.5 | 3.1 | 1.2 | 8.0 | 5.2 | 3.1 | 0.4 | 3.1 | 25.7 | 25.7 | +0.0 | 26.3 | +0.6 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference | 0.3 | 2.0 | 3.2 | 5.2 | 1.4 | 12.5 | 6.1 | 3.9 | 0.5 | 3.9 | 35.0 | 35.0 | +0.0 | 35.4 | +0.4 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) | 0.2 | 2.0 | 3.2 | 5.3 | 1.3 | 12.9 | 6.5 | 4.1 | 0.4 | 4.1 | 35.9 | 36.0 | +0.0 | 36.1 | +0.1 | +| Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference | n/r | n/r | n/r | n/r | n/r | n/r | n/r | 4.3 | n/r | 4.3 | 4.3* | 56.8 | +52.5* | not recorded | not recorded | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) | 0.1 | 1.1 | 1.5 | 1.3 | 1.1 | 3.3 | 3.7 | 1.1 | 0.3 | 1.1 | 13.5 | 13.4 | -0.0 | 13.8 | +0.4 | + +Mapping of steps to figure boxes: as under Table 2. + +- Mac mini: Apple M6, 12 cores (Apple M6 GPU (Metal, integration only)): the Mac report gives only the wall time and the MR step (4.3 s) for this set; no other step is in it (n/r). + +## Table 4: myoglobin (myob_x10sa), time per pipeline stage (s) + +rugnux's `TIMING step` lines of myob_x10sa's p_log.txt, grouped into the boxes of the workflow figure (docs/images/rugnux_pipeline.svg); the mapping is under Table 2. `-` = none of the stage's steps ran (from the log); `n/r` = a pack without logs reports none of them; `*` = it reports only some of them, so a sum marked `*` is partial. Wall - sum shows whether the steps cover rugnux's own clock (WALL_TIME). Elapsed (the battery's `elapsed_s`, from process launch to exit) minus wall is the time outside rugnux's clock: process start-up before step 1 and the exit after the last step. + +| Platform | GPU | run | Input | Pre-scan | Index | Per image, pass 1 | Post-refine | Per image, pass 2 | Scale & merge (P1) -> Symmetry -> Scale & merge | Structure & map | Report | of which Molecular replacement | **sum of steps** | **wall (WALL_TIME)** | wall - sum | elapsed (elapsed_s) | elapsed - wall | +|:--|:--|:--|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:|--:| +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) | 0.2 | 1.3 | 1.6 | 1.0 | 0.8 | 2.4 | 1.5 | 0.5 | 0.0 | 0.5 | 9.2 | 9.2 | -0.0 | 9.6 | +0.4 | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) | 0.1 | 6.2 | 12.8 | 11.9 | 0.7 | 27.3 | 4.4 | 1.8 | 0.0 | 1.8 | 65.4 | 65.4 | -0.0 | 65.7 | +0.3 | +| Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference | 0.2 | 3.6 | 3.7 | 1.2 | 1.8 | 2.5 | 2.8 | 0.8 | 0.1 | 0.8 | 16.9 | 16.9 | +0.0 | 18.0 | +1.1 | +| DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference | 0.2 | 2.3 | 3.0 | 2.1 | 1.1 | 4.8 | 2.5 | 1.0 | 0.1 | 1.0 | 17.1 | 17.1 | +0.0 | 17.7 | +0.6 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference | 0.3 | 2.4 | 3.8 | 3.4 | 2.4 | 7.3 | 2.1 | 1.5 | 0.1 | 1.5 | 23.4 | 23.4 | +0.0 | 27.7 | +4.3 | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) | 0.2 | 2.2 | 3.8 | 3.5 | 2.2 | 7.4 | 2.0 | 1.8 | 0.1 | 1.8 | 23.3 | 23.3 | +0.0 | 27.3 | +4.0 | +| Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference | n/r | n/r | n/r | n/r | n/r | n/r | n/r | 1.6 | n/r | 1.6 | 1.6* | 40.4 | +38.8* | not recorded | not recorded | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) | 0.1 | 1.3 | 1.7 | 1.0 | 0.9 | 2.1 | 1.5 | 0.4 | 0.0 | 0.4 | 9.2 | 9.2 | -0.0 | 9.5 | +0.3 | + +Mapping of steps to figure boxes: as under Table 2. + +- Mac mini: Apple M6, 12 cores (Apple M6 GPU (Metal, integration only)): the Mac report gives only the wall time and the MR step (1.6 s) for this set; no other step is in it (n/r). + +Where 4nwv's work ran and peak memory: + +| Platform | GPU | run | scaling | model validation | MR search | peak memory (myob / cytc / 4nwv) | +|:--|:--|:--|:--|:--|:--|:--| +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) | GPU | GPU (6.56 GB of a 13.29 GB budget) | GPU | 3.5 GiB RSS / 5.1 GiB RSS / 18.4 GiB RSS | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) | CPU | CPU | CPU | 9.9 GiB RSS / 10.0 GiB RSS / 26.8 GiB RSS | +| Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference | GPU | GPU (5.18 GB of a 12.52 GB budget) | GPU | not recorded | +| DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference | GPU | GPU (22.12 GB of a 104.53 GB budget) | GPU | not recorded | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference | GPU | GPU (4.42 GB of a 6.84 GB budget) | GPU | not recorded | +| Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) | GPU | GPU (4.42 GB of a 6.84 GB budget) | GPU | not recorded | +| Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference | CPU | CPU | CPU (no CUDA build) | 6.7 GiB RSS (~6.9 GiB footprint) / 5.9 GiB RSS (~10 GiB footprint) / 9.1 GiB RSS (~22 GiB footprint) | +| Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) | GPU | GPU (6.56 GB of a 13.29 GB budget) | GPU | not recorded | + +## Data + +The three sets of tools/battery/timing.json (copy: `timing.json` beside this report). Each is started from a model it has to place by molecular replacement: + +| set | arm | start model (--model) | expected: MR group (sgno), R_free max, placement CC min | +|:--|:--|:--|:--| +| myob_x10sa | inhouse | `tools/battery/models/AF-P68082-F1-model_v6.cif`: AlphaFold DB AF-P68082-F1 v6: myoglobin, Equus caballus (UniProt P68082), the species of the battery's reference model 4DC8 (horse heart). No heme | 4, 0.5, 0.3 | +| cytc_x10sa | inhouse | `tools/battery/models/AF-P62894-F1-model_v6.cif`: AlphaFold DB AF-P62894-F1 v6: cytochrome c, Bos taurus (UniProt P62894), the protein of the battery's reference model (bovine heart, placed with 6FF5). No heme; three copies to place, related by a pseudo-translation | 154, 0.5, 0.3 | +| 4nwv | open | deposited 4NWV from the site's pdb_cache, strict NCS expanded, turned 30.0 deg about the unit axis (0.36, 0.48, 0.8) through the centroid c of the expanded asymmetric unit and shifted t = (12.0, -18.0, 25.0) A (x' = R (x - c) + c + t), written as 4nwv_moved.cif.gz | 23, 0.35, 0.3 | + +Each platform's input path for each set is in its rugnux commands below. + +## Reproducibility, per platform and run + +### Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | reference (clean) + +- **pack**: run directory on the machine itself +- **run directory**: 20261016-0941_e25876_ws-3be5dc-timing +- **date/time**: 2026-10-16T09:41:50 to 2026-10-16T09:43:41 (the machine's local time) +- **host**: mpc2898.psi.ch +- **OS**: Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git e25876 Thu Oct 15 23:13:40 2026) - CUDA build +- **build flags (as the binary reports)**: -march=x86-64-v3 +- **build (battery manifest)**: Release CXX_FLAGS='-march=x86-64-v3' CUDA=ON; CMakeCache /data/tmp/rc175-metal-merge/build/CMakeCache.txt; source HEAD 2be738f557d985250bcbfad63f51735a069f799e; sha256 4ace4e1febbad7ff2502e5cb1295c3d70723e67424161934a1e22c9c250ce89f +- **compiler / CUDA / arch**: GCC 13.3.1 (gcc-toolset-13), nvcc 13.0.88, CUDA code for sm_75/80/86/89/90/100/120/121 (the default list; JFJOCH_CUDA_ARCHITECTURES empty), CMAKE_CXX_FLAGS/CMAKE_C_FLAGS = -march=x86-64-v3, no LTO; build dir /data/tmp/rc175-metal-merge/build (CMakeCache and CMakeFiles read 2026-10-16) +- **battery command**: `cd /home/jungfrau/nextgendcu && nice -n 0 python3 tools/battery/battery.py --site /data/battery/site.json timing --rugnux /data/tmp/rc175-metal-merge/build/rugnux/rugnux --gpulock --label ws-3be5dc-timing` +- **battery runner**: tools/battery at 2be738f55 +- **battery options (manifest)**: label ws-3be5dc-timing, threads 0 (0 = no -N), gpulock /data/battery/gpulock.sh, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 32 +- **hardware (manifest)**: CPU AMD Ryzen 9 5950X 16-Core Processor, 16 cores / 32 threads, 125.3 GiB RAM, GPU NVIDIA GeForce RTX 5080 (memory 16303 MiB) +- **GPU (rugnux reports)**: NVIDIA GeForce RTX 5080 (1 visible) +- **conditions**: started 09:41:50 right after a full battery ended (09:41); no other rugnux/battery/build process, GPU lock free, 0.3 GiB in use on the card before; load average 1.43 before, 6.70 after, highest 1-min load sampled during the run 9.75 (rugnux's own threads); page cache warm (the full battery had just read the same datasets); not the first run after the build +- **rugnux command, myob_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0941_e25876_ws-3be5dc-timing/bin/rugnux -o p --no-export-unmerged --report-resolution 1.742,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P68082-F1-model_v6.cif /ssd/data/inhouse/myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0941_e25876_ws-3be5dc-timing/bin/rugnux -o p --no-export-unmerged --report-resolution 2.039,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P62894-F1-model_v6.cif /ssd/data/inhouse/cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0941_e25876_ws-3be5dc-timing/bin/rugnux -o p --no-export-unmerged --model /data/battery/runs/20261016-0941_e25876_ws-3be5dc-timing/work/open/4nwv/4nwv_moved.cif.gz /ssd/data/open/4nwv/sbgrid9/formal.001` +- Peak RSS = VmHWM from /proc//status, sampled every 0.5 s. Whole-card GPU memory in use peaked at 8.1 GiB (nvidia-smi every 0.5 s; 0.3 GiB idle baseline). +- The binary reports git e25876; the rugnux code of e25876055 and 3be5dcaac is identical (the later commits change docs only). The battery runner was the shared checkout at 2be738f55 (3be5dcaac plus the Windows battery fixes). +- The manifest's 'source HEAD' is the build directory's source tree at run time (it had moved on to 2be738f55); the commit the binary was built from is the one it reports (e25876). + +### Workstation: AMD Ryzen 9 5950X (16C/32T) | none (--no-gpu) | reference (clean) + +- **pack**: run directory on the machine itself +- **run directory**: 20261016-0943_e25876_ws-3be5dc-timing-nogpu +- **date/time**: 2026-10-16T09:43:51 to 2026-10-16T09:55:13 (the machine's local time) +- **host**: mpc2898.psi.ch +- **OS**: Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git e25876 Thu Oct 15 23:13:40 2026) - CUDA build +- **build flags (as the binary reports)**: -march=x86-64-v3 +- **build (battery manifest)**: Release CXX_FLAGS='-march=x86-64-v3' CUDA=ON; CMakeCache /data/tmp/rc175-metal-merge/build/CMakeCache.txt; source HEAD 2be738f557d985250bcbfad63f51735a069f799e; sha256 4ace4e1febbad7ff2502e5cb1295c3d70723e67424161934a1e22c9c250ce89f +- **compiler / CUDA / arch**: GCC 13.3.1 (gcc-toolset-13), nvcc 13.0.88, CUDA code for sm_75/80/86/89/90/100/120/121 (the default list; JFJOCH_CUDA_ARCHITECTURES empty), CMAKE_CXX_FLAGS/CMAKE_C_FLAGS = -march=x86-64-v3, no LTO; build dir /data/tmp/rc175-metal-merge/build (CMakeCache and CMakeFiles read 2026-10-16) (same binary as the GPU run) +- **battery command**: `cd /home/jungfrau/nextgendcu && nice -n 0 python3 tools/battery/battery.py --site /data/battery/site.json timing --rugnux /data/tmp/rc175-metal-merge/build/rugnux/rugnux --gpulock --extra=--no-gpu --label ws-3be5dc-timing-nogpu` +- **battery runner**: tools/battery at 2be738f55 +- **battery options (manifest)**: label ws-3be5dc-timing-nogpu, threads 0 (0 = no -N), gpulock /data/battery/gpulock.sh, extra args ['--no-gpu'], timeout 10800 s +- **threads (rugnux reports)**: 32 +- **hardware (manifest)**: CPU AMD Ryzen 9 5950X 16-Core Processor, 16 cores / 32 threads, 125.3 GiB RAM, GPU NVIDIA GeForce RTX 5080 (memory 16303 MiB) +- **GPU (rugnux reports)**: not used (--no-gpu) - running on the CPU +- **conditions**: started 09:43:50 right after the GPU run, nothing else running; load average 5.67 before, 12.22 after, highest 1-min load sampled 34.1 during 4nwv (rugnux's own 32 threads); page cache warm +- **rugnux command, myob_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0943_e25876_ws-3be5dc-timing-nogpu/bin/rugnux -o p --no-export-unmerged --report-resolution 1.742,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P68082-F1-model_v6.cif --no-gpu /ssd/data/inhouse/myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0943_e25876_ws-3be5dc-timing-nogpu/bin/rugnux -o p --no-export-unmerged --report-resolution 2.039,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P62894-F1-model_v6.cif --no-gpu /ssd/data/inhouse/cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `/data/battery/gpulock.sh /data/battery/runs/20261016-0943_e25876_ws-3be5dc-timing-nogpu/bin/rugnux -o p --no-export-unmerged --model /data/battery/runs/20261016-0943_e25876_ws-3be5dc-timing-nogpu/work/open/4nwv/4nwv_moved.cif.gz --no-gpu /ssd/data/open/4nwv/sbgrid9/formal.001` +- Peak RSS = VmHWM from /proc, sampled every 0.5 s. +- Runner and binary as in the GPU run. +- The manifest's 'source HEAD' is the build directory's source tree at run time (it had moved on to 2be738f55); the commit the binary was built from is the one it reports (e25876). + +### Server: 4x Intel Xeon Gold 6146 (48C/48T recorded; rugnux used 24) | 4x Tesla T4 (15360 MiB each, as recorded) | reference + +- **pack**: bundle_rc175_3be5dc.tar.gz, bundle_rc175_3be5dc/b_mr_timing +- **run directory**: 20261016-0531_3be5dc_timing (on the server; b_mr_timing in the pack, name from its console.log) +- **date/time**: 2026-10-16T05:31:22 to 2026-10-16T05:36:23 (the machine's local time) +- **host**: mx-aare-test +- **OS**: not recorded +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git 3be5dc Fri Oct 16 00:55:26 2026) - CUDA build +- **build flags (as the binary reports)**: -march=x86-64-v3 +- **build (battery manifest)**: Release CXX_FLAGS='-march=x86-64-v3' CUDA=ON; CMakeCache build_v3/CMakeCache.txt; source HEAD 3be5dcaac8cf39a6871d0865efd669f608f4442d; sha256 00843ce39f5bda1e8b00d45f81f475f99251de2ffd6ef047c648c27ca881fb69 +- **compiler / CUDA / arch**: Release, -march=x86-64-v3, CUDA on (manifest; build dir build_v3); compiler, CUDA version, CUDA arch and LTO not recorded +- **battery command**: not recorded (the pack's console.log shows `battery.py timing` output; the manifest: label 'timing', no gpulock, no extra arguments) +- **battery runner**: tools/battery at 3be5dcaac +- **battery options (manifest)**: label timing, threads 0 (0 = no -N), gpulock None, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 24 +- **hardware (manifest)**: CPU Intel(R) Xeon(R) Gold 6146 CPU @ 3.20GHz, 48 cores / 48 threads, 1479.3 GiB RAM, GPU Tesla T4 (memory 15360 MiB), Tesla T4 (memory 15360 MiB), Tesla T4 (memory 15360 MiB), Tesla T4 (memory 15360 MiB) +- **GPU (rugnux reports)**: 4x Tesla T4 (4 visible) +- **conditions**: started 05:31:22, 5 s after a full battery run on the same machine ended (03:27:52 to 05:31:17, the pack's a_battery), so the page cache was presumably warm (not recorded as such); GPU not shared during that battery (its report); load average at 05:37:04, 40 s after the timing run ended: 0.52 / 3.85 / 6.32 (1 / 5 / 15 min), all four GPUs idle then (c_profile machine_before); load during the run not recorded; rugnux limits itself to 24 threads, the CPUs of the GPUs' sockets (24-47), and uses all 4 GPUs +- **rugnux command, myob_x10sa**: `/data/battery/runs/20261016-0531_3be5dc_timing/bin/rugnux -o p --no-export-unmerged --report-resolution 1.742,50.000 --model /home/jungfrau/jungfraujoch/tools/battery/models/AF-P68082-F1-model_v6.cif /data/inhouse/myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `/data/battery/runs/20261016-0531_3be5dc_timing/bin/rugnux -o p --no-export-unmerged --report-resolution 2.039,50.000 --model /home/jungfrau/jungfraujoch/tools/battery/models/AF-P62894-F1-model_v6.cif /data/inhouse/cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `/data/battery/runs/20261016-0531_3be5dc_timing/bin/rugnux -o p --no-export-unmerged --model /data/battery/runs/20261016-0531_3be5dc_timing/work/open/4nwv/4nwv_moved.cif.gz /data/open/4nwv/sbgrid9/formal.001` +- Peak memory not recorded. 4nwv model validation planned 5.18 GB of a 12.52 GB budget (80% of the card's 15.65 GB) and MR 4.79 GB, on the GPU. +- The pack also holds the full battery (a_battery) and nsys/perf profiles (c_profile); they are not used here. + +### DGX Spark: NVIDIA GB10 (ARM, 20 CPUs recorded) | GB10 (unified memory, 121.7 GiB recorded) | reference + +- **pack**: rugnux_gb10_perf_rc175-3be5dc_20261016.tar.gz, rugnux_gb10_perf_rc175-3be5dc_20261016/battery/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr +- **run directory**: 20261016-0944_3be5dc_gb10-rc175-3be5dc-mr +- **date/time**: 2026-10-16T09:44:46 to 2026-10-16T09:47:38 (the machine's local time) +- **host**: mx-spark-01 +- **OS**: not recorded (DGX Spark per the pack's ANALYSIS.md) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git 3be5dc Fri Oct 16 06:55:26 2026) - CUDA build +- **build flags (as the binary reports)**: none +- **build (battery manifest)**: Release CXX_FLAGS='' CUDA=ON; CMakeCache /home/jungfrau/jfj-rc175/build-rel/CMakeCache.txt; source HEAD 3be5dcaac8cf39a6871d0865efd669f608f4442d; sha256 0f536f07f21de2421688bb7651a73fdf4e5a1d2e4cfa464bb3e95d362803a10e +- **compiler / CUDA / arch**: Release, -DJFJOCH_RUGNUX_ONLY=ON -DJFJOCH_CUDA_ARCHITECTURES=121, no -march (ANALYSIS.md); LTO, compiler and CUDA versions not recorded +- **battery command**: not recorded (ANALYSIS.md: rc175's tools/battery `battery.py timing`, the pack's data, an absolute-path site config) +- **battery runner**: tools/battery at 3be5dcaac +- **battery options (manifest)**: label gb10-rc175-3be5dc-mr, threads 0 (0 = no -N), gpulock None, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 20 +- **hardware (manifest)**: CPU not recorded, not recorded cores / 20 threads, 121.7 GiB RAM, GPU NVIDIA GB10 (memory not recorded) +- **GPU (rugnux reports)**: NVIDIA GB10 (1 visible) +- **conditions**: GPU not shared: the resident GPU service was stopped for the whole session (ANALYSIS.md; the report says 'GPU was not shared during the run'); no gpulock; started 4 min after a six-set battery run on the same data (page cache state not recorded); load not recorded; the pack's pdb_cache JSONs were repointed by hand before this run +- **rugnux command, myob_x10sa**: `/home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/runs/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr/bin/rugnux -o p --no-export-unmerged --report-resolution 1.742,50.000 --model /home/jungfrau/jfj-rc175/tools/battery/models/AF-P68082-F1-model_v6.cif /home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/data/inhouse/myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `/home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/runs/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr/bin/rugnux -o p --no-export-unmerged --report-resolution 2.039,50.000 --model /home/jungfrau/jfj-rc175/tools/battery/models/AF-P62894-F1-model_v6.cif /home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/data/inhouse/cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `/home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/runs/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr/bin/rugnux -o p --no-export-unmerged --model /home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/runs/20261016-0944_3be5dc_gb10-rc175-3be5dc-mr/work/open/4nwv/4nwv_moved.cif.gz /home/jungfrau/jungfraujoch/battery/rugnux_battery_perf_20261014/data/open/4nwv/sbgrid9/formal.001` +- Peak memory not recorded. With unified memory the GPU budget is 104.53 GB: 4nwv model validation planned 22.12 GB (10 rigid-body engines), MR 21.60 GB (9 rigid-body engines). +- The same binary with the deposited 4nwv model (six-set run 20261016-0940_3be5dc_gb10-rc175-3be5dc in the same pack): 76.7 s. + +### Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | reference + +- **pack**: rugnux_win11_perf_3be5dc_20261016.tar.gz, rugnux_win11_perf_3be5dc_20261016/runs/1_timing +- **run directory**: 20261016-0753_3be5dc_3be5dc-timing-patched (on the laptop; runs/1_timing in the pack, name from its SOURCE_RUN.txt) +- **date/time**: 2026-10-16T07:53:42 to 2026-10-16T07:58:21 (the machine's local time) +- **host**: PC17975 +- **OS**: Windows 11 (pack name; build number not recorded); GPU in WDDM mode (BRIEF.md) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git 3be5dc Fri Oct 16 06:55:26 2026) - CUDA build +- **build flags (as the binary reports)**: /arch:AVX +- **build (battery manifest)**: Release CXX_FLAGS='/arch:AVX' CUDA=ON; CMakeCache build-win-ci\CMakeCache.txt; source HEAD 3be5dcaac8cf39a6871d0865efd669f608f4442d (tree dirty); sha256 ce0248ef8b9c5b270a886d0dfed682cc3533b8fc962cdf54f086c247165b77b8 +- **compiler / CUDA / arch**: MSVC 19.51 + CUDA 13.3, CI Windows settings /arch:AVX (BRIEF.md); CUDA arch and LTO not recorded; build dir build-win-ci +- **battery command**: not recorded (BRIEF.md: `battery.py timing` with the runner patched by battery-windows.patch, run from the pack's unmodified site.json) +- **battery runner**: tools/battery at 3be5dcaac (dirty) +- **battery options (manifest)**: label 3be5dc-timing-patched, threads 0 (0 = no -N), gpulock None, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 16 +- **hardware (manifest)**: CPU Intel(R) Core(TM) Ultra 7 255H, 16 cores / 16 threads, 31.4 GiB RAM, GPU NVIDIA RTX PRO 2000 Blackwell Generation Laptop GPU (memory 8151 MiB) +- **GPU (rugnux reports)**: NVIDIA RTX PRO 2000 Blackwell Generation Laptop GPU (1 visible) +- **conditions**: nothing else on the GPU, no gpulock (site gpulock null); the session's second timing run (the first is the next row); load and page cache not recorded +- **rugnux command, myob_x10sa**: `C:\Users\leonarski_f\rugnux_battery_perf_20261014\runs\20261016-0753_3be5dc_3be5dc-timing-patched\bin\rugnux.exe -o p --no-export-unmerged --report-resolution 1.742,50.000 --model C:\Users\leonarski_f\jungfraujoch\tools\battery\models/AF-P68082-F1-model_v6.cif C:\Users\leonarski_f\rugnux_battery_perf_20261014\data/inhouse\myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `C:\Users\leonarski_f\rugnux_battery_perf_20261014\runs\20261016-0753_3be5dc_3be5dc-timing-patched\bin\rugnux.exe -o p --no-export-unmerged --report-resolution 2.039,50.000 --model C:\Users\leonarski_f\jungfraujoch\tools\battery\models/AF-P62894-F1-model_v6.cif C:\Users\leonarski_f\rugnux_battery_perf_20261014\data/inhouse\cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `C:\Users\leonarski_f\rugnux_battery_perf_20261014\runs\20261016-0753_3be5dc_3be5dc-timing-patched\bin\rugnux.exe -o p --no-export-unmerged --model C:\Users\leonarski_f\rugnux_battery_perf_20261014\runs\20261016-0753_3be5dc_3be5dc-timing-patched\work\open\4nwv\4nwv_moved.cif.gz C:\Users\leonarski_f\rugnux_battery_perf_20261014\data/open\4nwv/sbgrid9/formal.001` +- Why this run is the reference: it is the run the pack's BRIEF.md reports as its result (`1_timing`, the patched runner from the pack's own site.json). The earlier run (next row) used the same binary (same sha256) and the unpatched runner. BRIEF.md puts the 167 vs 206 s 4nwv spread down to laptop thermals/power; that is its reading, not a measurement. + +### Laptop: Intel Core Ultra 7 255H (16C/16T) | RTX PRO 2000 Blackwell Laptop 8 GB | repeat (earlier run) + +- **pack**: rugnux_win11_perf_3be5dc_20261016.tar.gz, rugnux_win11_perf_3be5dc_20261016/runs/repeat_timing +- **run directory**: 20261016-0741_3be5dc_3be5dc-timing (on the laptop; runs/repeat_timing in the pack, name from its SOURCE_RUN.txt) +- **date/time**: 2026-10-16T07:41:16 to 2026-10-16T07:45:13 (the machine's local time) +- **host**: PC17975 +- **OS**: Windows 11 (pack name; build number not recorded) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git 3be5dc Fri Oct 16 06:55:26 2026) - CUDA build +- **build flags (as the binary reports)**: /arch:AVX +- **build (battery manifest)**: Release CXX_FLAGS='/arch:AVX' CUDA=ON; CMakeCache C:/Users/leonarski_f/jungfraujoch/build-win-ci\CMakeCache.txt; source HEAD 3be5dcaac8cf39a6871d0865efd669f608f4442d; sha256 ce0248ef8b9c5b270a886d0dfed682cc3533b8fc962cdf54f086c247165b77b8 +- **compiler / CUDA / arch**: same binary as the reference row (sha256 ce0248ef...) +- **battery command**: not recorded (BRIEF.md: the repo's unpatched runner with an absolute-path site config) +- **battery runner**: tools/battery at 3be5dcaac +- **battery options (manifest)**: label 3be5dc-timing, threads 0 (0 = no -N), gpulock None, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 16 +- **hardware (manifest)**: not recorded +- **GPU (rugnux reports)**: NVIDIA RTX PRO 2000 Blackwell Generation Laptop GPU (1 visible) +- **conditions**: the session's first timing run; nothing else on the GPU; load and page cache not recorded; hardware not recorded in this run's manifest (same host PC17975) +- **rugnux command, myob_x10sa**: `C:/Users/leonarski_f/rugnux_battery_perf_20261014/runs\20261016-0741_3be5dc_3be5dc-timing\bin\rugnux.exe -o p --no-export-unmerged --report-resolution 1.742,50.000 --model C:\Users\leonarski_f\jungfraujoch\tools\battery\models/AF-P68082-F1-model_v6.cif C:/Users/leonarski_f/rugnux_battery_perf_20261014/data/inhouse\myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `C:/Users/leonarski_f/rugnux_battery_perf_20261014/runs\20261016-0741_3be5dc_3be5dc-timing\bin\rugnux.exe -o p --no-export-unmerged --report-resolution 2.039,50.000 --model C:\Users\leonarski_f\jungfraujoch\tools\battery\models/AF-P62894-F1-model_v6.cif C:/Users/leonarski_f/rugnux_battery_perf_20261014/data/inhouse\cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `C:/Users/leonarski_f/rugnux_battery_perf_20261014/runs\20261016-0741_3be5dc_3be5dc-timing\bin\rugnux.exe -o p --no-export-unmerged --model C:/Users/leonarski_f/rugnux_battery_perf_20261014/runs\20261016-0741_3be5dc_3be5dc-timing\work\open\4nwv\4nwv_moved.cif.gz C:/Users/leonarski_f/rugnux_battery_perf_20261014/data/open\4nwv/sbgrid9/formal.001` + +### Mac mini: Apple M6, 12 cores | Apple M6 GPU (Metal, integration only) | reference + +- **run directory**: runs/20261016-0727_3be5dc_timing-3be5dc (on the Mac) +- **date/time**: 2026-10-16 07:27 start (from the run directory name); end not recorded +- **host**: not recorded +- **OS**: macOS (version not recorded) +- **rugnux (as the binary reports)**: not recorded (the report: origin/rc175 tip 3be5dcaac, metal-integration merged) +- **build flags (as the binary reports)**: not recorded +- **build (battery manifest)**: not recorded (the report: Release, no -march/LTO flags, JFJOCH_USE_CUDA=OFF, built in the jungfraujoch-origin-rc175 worktree) +- **compiler / CUDA / arch**: not recorded (no CUDA; Metal integration engine) +- **battery command**: not recorded (the report: `battery.py timing` with an absolute-path copy of the site config) +- **battery runner**: the repo's tools/battery at 3be5dcaac (the report), not patched +- **threads (rugnux reports)**: not recorded +- **hardware (manifest)**: Mac mini M6, 12 cores (performance/efficiency split not recorded), 16 GB (the report) +- **GPU (rugnux reports)**: not recorded +- **conditions**: nothing else running; data read cold (the report) +- **rugnux command, myob_x10sa**: not recorded +- **rugnux command, cytc_x10sa**: not recorded +- **rugnux command, 4nwv**: not recorded +- Peak memory sampled once a second by the Mac agent (ps RSS; top's physical footprint, rounded to whole GiB above 10 GiB). +- The pack's 4nwv pdb_cache JSON had to be corrected by hand before 4nwv would run. + +### Workstation: AMD Ryzen 9 5950X (16C/32T) | RTX 5080 16 GB | earlier run (10-15 23:26) + +- **pack**: run directory on the machine itself +- **run directory**: 20261015-2326_e25876_rc175-r10-timing +- **date/time**: 2026-10-15T23:26:18 to 2026-10-15T23:28:11 (the machine's local time) +- **host**: mpc2898.psi.ch +- **OS**: Rocky Linux 8.10, kernel 4.18.0-553.22.1.el8_10.x86_64, NVIDIA driver 580.82.07 (read 2026-10-16, after the runs) +- **rugnux (as the binary reports)**: rugnux Version 1.0.0-rc.175 (git e25876 Thu Oct 15 23:13:40 2026) - CUDA build +- **build flags (as the binary reports)**: -march=x86-64-v3 +- **build (battery manifest)**: Release CXX_FLAGS='-march=x86-64-v3' CUDA=ON; CMakeCache /data/tmp/rc175-metal-merge/build/CMakeCache.txt; source HEAD e25876055feaff22bd10c092f911019e5d0ec54d; sha256 4ace4e1febbad7ff2502e5cb1295c3d70723e67424161934a1e22c9c250ce89f +- **compiler / CUDA / arch**: same binary as the clean workstation runs (sha256 identical) +- **battery command**: not recorded (options in the manifest below) +- **battery runner**: tools/battery at 0a8eda37f +- **battery options (manifest)**: label rc175-r10-timing, threads 0 (0 = no -N), gpulock /data/battery/gpulock.sh, extra args [], timeout 10800 s +- **threads (rugnux reports)**: 32 +- **hardware (manifest)**: CPU AMD Ryzen 9 5950X 16-Core Processor, 16 cores / 32 threads, 125.3 GiB RAM, GPU NVIDIA GeForce RTX 5080 (memory 16303 MiB) +- **GPU (rugnux reports)**: NVIDIA GeForce RTX 5080 (1 visible) +- **conditions**: described by the owner as taken while the machine was heavily loaded by other agents (load 15-35); load during this run not recorded; its times are within 1% of the clean run +- **rugnux command, myob_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261015-2326_e25876_rc175-r10-timing/bin/rugnux -o p --no-export-unmerged --report-resolution 1.742,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P68082-F1-model_v6.cif /ssd/data/inhouse/myob_x10sa/MyoB_13_110ee0_master.h5` +- **rugnux command, cytc_x10sa**: `/data/battery/gpulock.sh /data/battery/runs/20261015-2326_e25876_rc175-r10-timing/bin/rugnux -o p --no-export-unmerged --report-resolution 2.039,50.000 --model /home/jungfrau/nextgendcu/tools/battery/models/AF-P62894-F1-model_v6.cif /ssd/data/inhouse/cytc_x10sa/cytC_10_002_master.h5` +- **rugnux command, 4nwv**: `/data/battery/gpulock.sh /data/battery/runs/20261015-2326_e25876_rc175-r10-timing/bin/rugnux -o p --no-export-unmerged --model /data/battery/runs/20261015-2326_e25876_rc175-r10-timing/work/open/4nwv/4nwv_moved.cif.gz /ssd/data/open/4nwv/sbgrid9/formal.001` +- Same binary as the clean runs: identical sha256 (4ace4e1f...); at this run the source tree was still at e25876055. + +## What stands out + +- Every check passed on every platform and run (server and GB10 included) (6 of 6 checks on each of the 3 sets). The MR decisions are the same everywhere: myob and 4nwv AGREES. For cytc the data give P 31 2 1 and MR places the model in P 32 2 1 (ENANTIOMORPH); the Mac report gives the same hand but no MR_DECISION key. +- The steps cover rugnux's whole clock on every platform: on 4nwv the sum of the TIMING steps equals WALL_TIME to 0.1 s. Outside that clock, process launch to exit (elapsed_s) adds 0.5-1.0 s. There is no hidden time between steps. +- On Windows, the first set of a run (myob_x10sa) has 4.0-4.3 s between process launch and exit that rugnux's own clock does not cover; the later sets and every other platform have 0.1-1.0 s. What that time is (process or CUDA start-up before step 1, say) is not recorded. +- Workstation (RTX 5080): 4nwv takes 84 s. Scaling and merging is the biggest step (25 s), then MR (20 s), then integration (10.5 s). The run of 10-15 23:26, said to be on a loaded machine, is within 1% of the clean run. So load did not inflate that particular run, whatever it did to others. +- Workstation (RTX 5080): 4nwv takes 84 s. Scale & merge (P1 merge, symmetry, final merge, analyses) is the largest stage at 28 s. Structure & map follows at 27 s (MR 20 s of it), then post-refine at 8 s and the two per-image passes at 10.5 s together. The run of 10-15 23:26, said to be on a loaded machine, is within 1% of the clean run, and used the identical binary. So load did not inflate that particular run, whatever it did to others. +- Workstation CPU only: 4nwv takes 515 s, 6.2x the GPU run. Structure & map takes 331 s of it (MR 243 s, about 12x the GPU), and the per-image passes 83 s (8x). Scale & merge is only 2x slower (56 s). The two small sets are 7x slower. +- Mac M6 (CPU + Metal integration): 4nwv takes 431 s, 0.84x the workstation's CPU-only time. Its MR runs on the CPU and takes as long as the workstation's CPU-only MR (240 vs 243 s); it is 56% of the Mac's wall time. Metal brings the per-image passes to 55 s (CPU-only workstation: 83 s). Model validation (56 s) and scale & merge (49 s) also beat the 16-core CPU-only run. Caveat: the Mac binary has no -march flags, the workstation's has x86-64-v3. +- Windows laptop (RTX PRO 2000, 8 GB): 4nwv takes 167-206 s, 2.0-2.5x the workstation GPU run. Every stage is slower: per-image passes 27 s (2.6x), scale & merge 40-58 s, structure & map 71-87 s (MR 52-62 s, 2.7-3.1x). Back-to-back runs differ by 23%, spread over the GPU/CPU-heavy stages. The laptop also spends 2.6-5.6 s writing files and 3.3-4.4 s in the geometry-walk probe (0.5 and 0.1 s on the workstation). +- GB10 (DGX Spark, unified memory): 4nwv takes 123 s, 1.47x the RTX 5080 workstation and faster than the laptop (167-206 s). By stage against the workstation: scale & merge is nearly level (29.0 vs 27.9 s), the per-image passes are 1.4x (15.1 vs 10.5 s), and structure & map is 2.3x (61.0 vs 27.1 s; MR alone 2.4x, 46.6 vs 19.7 s). MR is its largest step, 38% of the wall time; per the pack's profile the GPU rotation function alone takes 25.7 s. The small sets lose more: myob 17.1 s and cytc 25.7 s, 1.9x the workstation. Everything ran on the GPU. With a 104.5 GB budget, model validation planned 22.1 GB with 10 rigid-body engines and MR 21.6 GB with 9; the 8 and 16 GB cards ran 1-2. +- Server (4x Xeon Gold 6146, 4x T4; rugnux uses the 24 CPUs of the GPUs' sockets and all four cards): 4nwv takes 248 s. That is 3.0x the RTX 5080 workstation and slower than the 8 GB laptop (167-206 s). The gap is almost all structure & map: 150 s, of which MR is 123 s (6.3x the workstation's 19.7 s, 2.0-2.4x the laptop's) and model validation 26 s (3.5x). Scale & merge (57 s) and post-refine (16 s) are about 2x the workstation. The two per-image passes, spread over four cards, are level with it (11.0 vs 10.5 s). The small sets match GB10: myob 16.9 s and cytc 25.8 s (GB10: 17.1 and 25.7 s), 1.8-2.0x the workstation, with pre-scan and indexing the largest stages. The run started 5 s after a full battery on the same machine ended; the load during it was not recorded. +- Peak host memory for 4nwv: workstation GPU 18.4 GiB RSS, workstation CPU-only 26.8 GiB, Mac 9.1 GiB RSS (~22 GiB physical footprint). The Windows, GB10 and server packs recorded none. The two numbers are not directly comparable: Mac RSS vs footprint, unified memory. diff --git a/tools/battery/results/timing_platforms/timing_table.py b/tools/battery/results/timing_platforms/timing_table.py new file mode 100644 index 000000000..186dec2f4 --- /dev/null +++ b/tools/battery/results/timing_platforms/timing_table.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python3 +"""Cross-platform report of the rugnux timing benchmark (battery.py timing). + + python3 -I timing_table.py platforms.json [-o out.md] + +platforms.json: {"title", "intro", "timing_json", "stands_out": [str], "platforms": [entry]}. +"timing_json" is a copy of tools/battery/timing.json (path relative to platforms.json), for the data +section. An entry is {"platform" (short name + CPU), "gpu", "role", "conditions", "os", "build", +"battery_cmd", "notes": [str], "peak_mem": {set: str}, "table2_note"} plus ONE source: + "run": a battery run directory (results.json + manifest.json; logs under logs// or + work///), + "archive": a .tar.gz/.tar plus "member", the run directory's path inside it; the archive is unpacked + once into unpacked// next to this script (members with absolute paths, '..' + or links are refused), + "manual": typed in from a report with no run directory: {"sets": {set: {"verdict", "wall", + "mr_decision", "steps": {step: s}}}, "repro": {field: text}}. +Whatever a source does not record is printed as "not recorded". Nothing here judges: the checks are +the benchmark's own (bench_checks in results.json). +""" +import json +import os +import re +import sys +import tarfile + +SETS = ["myob_x10sa", "cytc_x10sa", "4nwv"] +HERE = os.path.dirname(os.path.abspath(__file__)) +NR = "not recorded" + + +def unpack(archive, member): + name = re.sub(r"\.tar(\.gz)?$", "", os.path.basename(archive)) + dest = os.path.join(HERE, "unpacked", name) + if not os.path.isdir(dest): + os.makedirs(dest) + with tarfile.open(archive) as t: + safe = [m for m in t.getmembers() + if not m.name.startswith("/") and ".." not in m.name.split("/") and (m.isfile() or m.isdir())] + kw = {"filter": "data"} if hasattr(tarfile, "data_filter") else {} + t.extractall(dest, members=safe, **kw) + return os.path.join(dest, member) + + +def log_dir(run, row): + for d in (os.path.join(run, "logs", row["set"]), os.path.join(run, "work", row.get("arm", ""), row["set"])): + if os.path.isdir(d): + return d + return None + + +def from_log(d): + """The binary's own header lines and where the work ran.""" + out = {} + log = d and os.path.join(d, "p_log.txt") + if not log or not os.path.exists(log): + return out + text = open(log, errors="replace").read() + for key, pat in (("version", r"\] (rugnux Version [^\n]*)"), ("build_flags", r"\] Build flags: ([^\n]*)"), + ("gpu", r"\] GPU: ([^\n]*)"), ("threads", r"\] Threads: (\d+)")): + m = re.search(pat, text) + out[key] = m.group(1).strip() if m else None + out["scaling"] = "GPU" if "RotationScaleMerge: GPU scaling" in text else "CPU" + m = re.search(r"Model validation: structure factors[^\n]*?([\d.]+ GB) held of a ([\d.]+ GB) budget", text) + out["mv"] = f"GPU ({m.group(1)} of a {m.group(2)} budget)" if m else "CPU" + rep = os.path.join(d, "p_report.txt") + if os.path.exists(rep): + for line in open(rep, errors="replace"): + if line.startswith("MR_SEARCH_DEVICE="): + out["mr_dev"] = line.split("=", 1)[1].strip() + return out + + +def load(p): + """{set: row}, {repro field: text}""" + if "manual" in p: + return p["manual"]["sets"], dict(p["manual"].get("repro", {})) + run = p["run"] if "run" in p else unpack(p["archive"], p["member"]) + man = json.load(open(os.path.join(run, "manifest.json"))) + sets = {} + for r in json.load(open(os.path.join(run, "results.json"))): + bad = [k for k, v in (r.get("bench_checks") or {}).items() if not v[0]] + sets[r["set"]] = {"verdict": r.get("bench_verdict") or "-", "failed": bad, "wall": r.get("wall_s"), + "elapsed": r.get("elapsed_s"), "mr_decision": r.get("mr_decision"), + "steps": r.get("steps") or {}, "cmd": r.get("cmd"), **from_log(log_dir(run, r))} + b, o, hw = man.get("binary", {}), man.get("options", {}), man.get("hardware") or {} + first = sets.get("4nwv") or next(iter(sets.values())) + gpus = ", ".join(f"{g.get('name')} (memory {rec(g.get('memory'))})" for g in hw.get("gpus", [])) or None + repro = { + "pack": (f"{os.path.basename(p['archive'])}, {p['member']}" if "archive" in p else "run directory on the machine itself"), + "run directory": p.get("run_name") or os.path.basename(os.path.normpath(run)), + "date/time": f"{man.get('start')} to {man.get('end')} (the machine's local time)", + "host": man.get("host"), + "rugnux (as the binary reports)": first.get("version"), + "build flags (as the binary reports)": first.get("build_flags"), + "build (battery manifest)": f"{b.get('flags')}; CMakeCache {b.get('cmake_cache')}; source HEAD {b.get('source_head')}" + f"{' (tree dirty)' if b.get('source_dirty') else ''}; sha256 {b.get('sha256')}", + "battery runner": f"tools/battery at {man.get('runner_git')}{' (dirty)' if man.get('runner_dirty') else ''}", + "battery options (manifest)": f"label {man.get('label')}, threads {o.get('threads')} (0 = no -N), gpulock " + f"{o.get('gpulock')}, extra args {o.get('extra_args')}, timeout {o.get('timeout')} s", + "threads (rugnux reports)": "/".join(sorted({r.get("threads") or "?" for r in sets.values()})), + "hardware (manifest)": (f"CPU {rec(hw.get('cpu_model'))}, {rec(hw.get('physical_cores'))} cores / {rec(hw.get('threads'))} threads, " + f"{hw.get('memory_gib'):.1f} GiB RAM, GPU {gpus}") if hw else None, + "GPU (rugnux reports)": first.get("gpu"), + } + for s in SETS: + if s in sets: + repro[f"rugnux command, {s}"] = f"`{sets[s]['cmd']}`" if sets[s].get("cmd") else None + return sets, repro + + +def rec(x): + return NR if x in (None, "unknown", "[N/A]") else x + + +def stage_table(L, cfg, data, SET, num, title): + groups = [(g["box"], g["steps"]) for g in cfg["step_groups"]] + mapped = {k for _, ks in groups for k in ks} + for _, d, _ in data: # a step no group names keeps a row of its own, so nothing is dropped + for k in d.get(SET, {}).get("steps", {}): + if k not in mapped: + groups.append((f"(not in the figure) {k}", [k])) + mapped.add(k) + extra = cfg.get("step_extra_columns", []) # [label, step]: one step shown again on its own + L += ["", f"## Table {num}: {title}, time per pipeline stage (s)", "", + "rugnux's `TIMING step` lines of " + SET + "'s p_log.txt, grouped into the boxes of the workflow figure " + "(docs/images/rugnux_pipeline.svg); the mapping is " + ("below" if num == 2 else "under Table 2") + ". `-` = none of the stage's steps ran (from " + "the log); `n/r` = a pack without logs reports none of them; `*` = it reports only some of them, so a sum " + "marked `*` is partial. Wall - sum shows whether the steps cover rugnux's own " + "clock (WALL_TIME). Elapsed (the battery's `elapsed_s`, from process launch to exit) minus wall is the time " + "outside rugnux's clock: process start-up before step 1 and the exit after the last step.", ""] + logged = {k for p, d, _ in data if "manual" not in p for k in d.get(SET, {}).get("steps", {})} + hdr = (["Platform", "GPU", "run"] + [g for g, _ in groups] + [lab for lab, _ in extra] + + ["**sum of steps**", "**wall (WALL_TIME)**", "wall - sum", "elapsed (elapsed_s)", "elapsed - wall"]) + L += ["| " + " | ".join(hdr) + " |", "|:--|:--|:--|" + "--:|" * (len(hdr) - 3)] + for p, d, _ in data: + r = d.get(SET, {}) + st = r.get("steps", {}) + s = sum(st.values()) if st else None + w, e = r.get("wall"), r.get("elapsed") + cells = [p["platform"], p["gpu"], p.get("role", "")] + partial = False + for _, ks in groups: + have = [k for k in ks if k in st] + if "manual" in p: # a typed-in report: a step it does not list (but a log has) is unknown + ks = [k for k in ks if k in logged or k in st] + cells.append("n/r" if not have else f(sum(st[k] for k in have)) + ("*" if len(have) < len(ks) else "")) + partial = partial or len(have) < len(ks) + else: # from the log: a step that is not there did not run + cells.append(f(sum(st[k] for k in have)) if have else "-") + cells += [f(st[k]) if k in st else "n/r" for _, k in extra] + star = "*" if partial else "" + cells += [f(s) + star, f(w), (f"{w - s:+.1f}" + star) if s is not None and w is not None else "-", + f(e) if e else NR, f"{e - w:+.1f}" if e and w else NR] + L.append("| " + " | ".join(cells) + " |") + if num != 2: + L += ["", "Mapping of steps to figure boxes: as under Table 2."] + else: + L += ["", "Figure box <- rugnux steps (what each step covers, from rugnux/*.cpp):", ""] + L += [f"- **{g['box']}** <- {' + '.join(g['steps'])}{': ' + g['why'] if g.get('why') else ''}" for g in cfg["step_groups"]] + L += [f"- {n}" for n in cfg.get("step_notes", [])] + notes = [f"- {p['platform']} ({p['gpu']}): {p['table_notes'][SET]}" for p, _, _ in data if p.get("table_notes", {}).get(SET)] + if notes: + L += [""] + notes + + +def f(x): + return "-" if x is None else f"{x:.1f}" + + +def main(): + cfg_path = sys.argv[1] + cfg = json.load(open(cfg_path)) + out_path = sys.argv[sys.argv.index("-o") + 1] if "-o" in sys.argv else None + data = [(p, *load(p)) for p in cfg["platforms"]] + L = [f"# {cfg['title']}", "", cfg.get("intro", ""), ""] + + L += ["## Table 1: wall time (s), timing benchmark", "", + "Wall = rugnux's own WALL_TIME. Checks = the benchmark's six checks on each set (space_group, placed, " + "mr_group, r_free, confirmed, placement_cc); a failing check would be named.", "", + "| Platform | GPU | run | myob_x10sa | cytc_x10sa | 4nwv | checks | MR decision (myob / cytc / 4nwv) |", + "|:--|:--|:--|--:|--:|--:|:--|:--|"] + for p, d, _ in data: + ver = [d[s]["verdict"] + (" (" + ", ".join(d[s]["failed"]) + ")" if d[s].get("failed") else "") + for s in SETS if s in d] + checks = "all pass" if len(ver) == 3 and all(v == "pass" for v in ver) else " / ".join(ver) + L.append(f"| {p['platform']} | {p['gpu']} | {p.get('role', '')} | " + + " | ".join(f(d.get(s, {}).get("wall")) for s in SETS) + + f" | {checks} | " + " / ".join(d.get(s, {}).get("mr_decision") or NR for s in SETS) + " |") + + for num, s_, title in ((2, "4nwv", "4nwv"), (3, "cytc_x10sa", "cytochrome C (cytc_x10sa)"), (4, "myob_x10sa", "myoglobin (myob_x10sa)")): + stage_table(L, cfg, data, s_, num, title) + L += ["", "Where 4nwv's work ran and peak memory:", "", + "| Platform | GPU | run | scaling | model validation | MR search | peak memory (myob / cytc / 4nwv) |", + "|:--|:--|:--|:--|:--|:--|:--|"] + for p, d, _ in data: + r = d.get("4nwv", {}) + dev = [r.get("scaling"), r.get("mv"), r.get("mr_dev")] if r.get("scaling") else p.get("devices", [NR] * 3) + pm = p.get("peak_mem") + L.append(f"| {p['platform']} | {p['gpu']} | {p.get('role', '')} | " + " | ".join(x or NR for x in dev) + + " | " + (" / ".join(pm.get(s, NR) for s in SETS) if pm else NR) + " |") + + tj = json.load(open(os.path.join(os.path.dirname(os.path.abspath(cfg_path)), cfg["timing_json"]))) + L += ["", "## Data", "", + f"The three sets of tools/battery/timing.json (copy: `{cfg['timing_json']}` beside this report). Each " + "is started from a model it has to place by molecular replacement:", "", + "| set | arm | start model (--model) | expected: MR group (sgno), R_free max, placement CC min |", + "|:--|:--|:--|:--|"] + for t in tj["sets"]: + st = t["start"] + if "alphafold" in st: + sm = f"`tools/battery/{st['alphafold']}`: {st['why']}" + else: + mv = st["moved"] + sm = (f"deposited {mv['pdb'].upper()} from the site's pdb_cache, strict NCS expanded, turned {mv['deg']} deg " + f"about the unit axis {tuple(mv['axis'])} through the centroid c of the expanded asymmetric unit and " + f"shifted t = {tuple(mv['shift'])} A (x' = R (x - c) + c + t), written as 4nwv_moved.cif.gz") + e = t["expect"] + L.append(f"| {t['set']} | {t['arm']} | {sm} | {e['mr_sgno']}, {e['rfree_max']}, {e['cc_min']} |") + L += ["", "Each platform's input path for each set is in its rugnux commands below."] + + L += ["", "## Reproducibility, per platform and run", ""] + order = ["pack", "run directory", "date/time", "host", "OS", "rugnux (as the binary reports)", + "build flags (as the binary reports)", "build (battery manifest)", "compiler / CUDA / arch", + "battery command", "battery runner", "battery options (manifest)", "threads (rugnux reports)", + "hardware (manifest)", "GPU (rugnux reports)", "conditions"] + [f"rugnux command, {s}" for s in SETS] + for p, d, repro in data: + for k, field in (("OS", "os"), ("compiler / CUDA / arch", "build"), ("battery command", "battery_cmd"), + ("conditions", "conditions")): + repro.setdefault(k, p.get(field)) + L += [f"### {p['platform']} | {p['gpu']} | {p.get('role', '')}", ""] + L += [f"- **{k}**: {repro.get(k) or NR}" for k in order if k in repro] + L += [f"- **{k}**: {v or NR}" for k, v in repro.items() if k not in order] + L += [f"- {n}" for n in p.get("notes", [])] + [""] + + if cfg.get("stands_out"): + L += ["## What stands out", ""] + [f"- {x}" for x in cfg["stands_out"]] + text = "\n".join(L) + "\n" + if out_path: + open(out_path, "w").write(text) + print(text) + + +if __name__ == "__main__": + main()