You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Ledger (82): the per-block AMR cost is amdflang's per-launch re-map of allocatable derived-type arrays; per-file defaultmap(present:allocatable) opt-in for m_amr, -8%/-18% wall, bit-identical, gated
Copy file name to clipboardExpand all lines: docs/documentation/amr_action_plan.md
+106Lines changed: 106 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -226,6 +226,112 @@ possible while AMR aborts on the target machine at 1 rank, and every increment b
226
226
on a compiler that does not reproduce it. It also means the ladder should add a CCE arm as soon as one
227
227
exists, or the same class of breakage will keep accumulating undetected.
228
228
229
+
## 2026-09-05 (82) — THE PER-BLOCK COST IS NAMED: amdflang re-maps every allocatable array of derived type a kernel touches on EVERY launch, ~0.3 ms per 10-component array, linear in the component count; host geometry is nil; the fix is one line in the AMD macro lane
230
+
231
+
**The question (ledger 81's next lever).** Ledger 80 priced the per-block AMR overhead at ~13 ms/block/step and ledger 81
232
+
showed kernel time is invariant to a 15%% dispatch cut, so the ~4 ms/block/step in gather+gfill had to be NON-kernel time:
233
+
host per-block work or launch/map overhead. This entry measured which, then measured why.
234
+
235
+
**1. Host profile (0dce9d0a, five bracket-free host rows in the [mpiwait] table, both fill waves, OFF path).** Same
236
+
clock as the MPI-wait rows, no MPI inside, on the default deck (amr_device_pack OFF, unlike ledgers 80-81's device_pack=T
237
+
arms -- so the gather slope here starts at 7.6, not their ~2.9): h:slot (region/parent geometry + box intersect), h:shell (shell slabs + clip),
| explicit-shape derived-type dummy q(sys_size), unit clean of implicit maps | 26 |
267
+
268
+
The mechanism is compile-time and per compilation unit (T2/T3/T5, and `nm`: the `.omp_mapper._QQMmdtsf_t_omp_default_mapper`
269
+
symbol exists exactly in the binaries/objects that hold an implicit map of an sf_t array -- T3, dt_cost, t_a, s0, T5_other.o --
270
+
and in none of T5_main.o, T2, T1, s1, t_b3; outputs archived in amr-bench/ubench/results_2026-09-05_k004-001.txt): the moment ANY target region in a unit implicitly maps
271
+
an allocatable array of a derived type, flang emits a per-element mapper for that type and every map of that type in the
272
+
unit -- module arrays, explicit-shape dummies of it, nested arrays of it -- walks and re-attaches each component on every
273
+
launch. This is the 0.31 ms/launch the fill bracket carries over its kernel, it is why the pooled kernels of ledger 81
274
+
(amr_cgp with blocks x sys_size components) cost MORE per launch with fewer launches, and it is the m_ibm.fpp:1827
275
+
per-element-mapper failure in a milder form. `defaultmap(present: allocatable)` on the directive asserts the arrays
276
+
present with no map entry, so no mapper is generated: it is exactly what CCE's ``default='present'`` has always emitted,
277
+
and the AMD lane has emitted NOTHING since the commit that added the AMD lane (d52a...b2b, "Add AMD compiler support"). amdflang accepts ONE defaultmap per directive (aggregate+allocatable,
278
+
a split target/teams pair, `all`, and present+firstprivate:scalar are all rejected or fault); `has_device_addr` also
279
+
works on module arrays (19 us, verified) but faults on dummies and crashes the frontend on nested types -- not used.
280
+
281
+
**3. The increment (00a7c569 = 0dce9d0a + a per-FILE opt-in).** The first form -- the clause on every AMD kernel
282
+
(08da1931) -- aborts at init: the conservative-to-primitive conversion kernel in m_variables_conversion names a module
283
+
allocatable that is unallocated in this case (the implicit map maps 0 bytes; `present` refuses a null address). Because
284
+
the mapper tax is per compilation unit, the increment is a file-level switch: OMP_DEFAULT_STR emits
285
+
`defaultmap(present:allocatable)` on AMD only where the file sets `MFC_OMP_PRESENT_ALLOCATABLE`, and m_amr.fpp sets it
286
+
(audited: the only bare module allocatables its 95 kernels name are amr_cg and amr_cons_br, both allocated
287
+
unconditionally before first use; every conditionally allocated one -- amr_rvw, sw_jac, jac, amr_cg_pb/mv, amr_prim_st,
288
+
amr_bt_*, amr_gst_* -- is declare-target (GPU_DECLARE), and declare-target allocatables are exempt: amr_rvw is unallocated on this deck,
289
+
named under the clause in s_amr_restrict_overwrite_device_sf, and that kernel ran 400-1,125 times/rank without aborting).
290
+
The 08da1931 abort was in fact the chemistry-only COMPONENT q_T_sf%%sf (m_time_steppers.fpp:296-307) named by the
291
+
conversion kernel, so the audit rule for any unit that opts in is: no kernel may name an allocatable VARIABLE OR
292
+
COMPONENT that can be unallocated at launch unless it is declare-target. m_ibm keeps its four per-kernel copies. Net +14
293
+
LOC (9 of them comment), no runtime flag: a compiler-lane default, like CCE's.
294
+
295
+
**4. Result (00a7c569 vs 0dce9d0a: same source but for the switch, same deck, same hold job, arms 64/32/64 each,
296
+
per-rank means over 8 ranks; per-launch rows from the second cap-64 arm of each chain, the first arms read
297
+
0.437->0.111, 2.030->0.612, 0.474->0.314).**
298
+
299
+
|| cap 64 | cap 32 |
300
+
|---|---|---|
301
+
| step-loop wall, s | 38.28 / 37.68 -> 34.16 / 35.64 (-5 to -11%% pairwise, means -8%%) | 61.73 -> 50.62 (-18%%) |
failed, TOUCHED=0. Oracle np=2: F57C3A5B and EF58E377 both 6 families, 0 unbalanced, 0 mismatches, seed controls PASS.
323
+
CPU build: passes at dede9e33 (mfc-amr-cpu, --no-gpu). Independent review before this was written: no code blocker; its corrections (the -5..-11%%
324
+
range, +14 LOC, device_pack=OFF stated, no subtraction from ledger 80's device_pack=T number, the audit rule) are applied.
325
+
NVHPC/CCE/OpenACC lanes: the only functional change is inside the AMD branch of OMP_DEFAULT_STR; the other branches are
326
+
byte-identical to before. Landed on up/mega as ONE squashed lane commit (bbec0451, the global form, does not build; the three lane
327
+
commits and the gated hashes stay on origin/task13/host-profile); no flag, since a compiler-lane default is not a behaviour
328
+
change of the solver (bit-identity above).
329
+
330
+
**What this does NOT claim.** Nothing about NVHPC or CCE changes (their lanes emit what they emitted). The per-block cost
331
+
outside gather/gfill (rhs per-batch ~4.6, regrid ~2, reflux 2.0, seam 1.6 ms/block/step, ledger 80) is not yet
332
+
re-measured under the clause; the census counted 540 launches/rank/step at cap 64, so the same tax sits in the
333
+
block-count-independent floor too, and the whole-step number is the one that matters.
334
+
229
335
## 2026-09-05 (81) — NEGATIVE, PRE-REGISTERED, FALSIFIER FIRED: pooling the gather consume into three launches per wave is bit-identical and saves nothing on the first attempt -- the pooled kernels cost more per block than what they replaced, and gather's residual cannot be apportioned from these runs
230
336
231
337
**The increment (parked on `task12/batched-gather`, NOT merged).** Behind the default-off `amr_batched_gather` (requires
0 commit comments