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
FNL/OpenACC PRISM run segfaults inside FUNDAL dev_assign_to_device, on the coil object's first host→device transfer. The fault appears only above a grid-size threshold, and that threshold is reported to be well below the VRAM limit. Not reproducible on the WSL dev box. No measurement was possible before the cluster went down, so the trigger below is derived from code inspection, not from a captured trace.
The path that actually runs
prism_fnl_object%copy_cpu_gpu calls the coil copy withoutbuf6D:
buf6D is optional and is never passed by any caller (:328, :349, and adam_prism_fnl_coil_object.F90:133). So the buffered branch at adam_prism_fnl_coil_object.F90:63-66 is dead, and every coil transfer takes the fallback:
Step 2 allocates a second full-size host copy of j_vec. Between steps 2 and 4 the process holds j_vecandsrct simultaneously.
j_vec is (3, 1-ngc:ni+ngc, 1-ngc:nj+ngc, 1-ngc:nk+ngc, nb, nc) (src/app/prism/common/adam_prism_coil_object.F90:134), so the transient scales as 3·(ni+2ngc)·(nj+2ngc)·(nk+2ngc)·nb·nc·8 bytes — a clean "grows with grid dimensions, crosses a threshold, dies" signature that is host RAM, not VRAM. That is consistent with the reported threshold sitting far below the VRAM limit.
Why cluster-only is plausible: WSL is one rank against a large flat pool; a cluster node runs N ranks sharing node RAM, multiplying the same per-rank transient.
transpose_array_alloc uses a plain allocate with no stat=, so exhaustion aborts inside a FUNDAL routine bracketed by device calls — which reads as a device fault.
This is a hypothesis consistent with the code and the reported symptom. It is not confirmed against a trace.
Defect 2 — ierr is discarded everywhere, converting OOM into SIGSEGV
fundal_dev_assign_agnostic.INC:493 captures ierr into a local and never tests it, then unconditionally calls dev_memcpy_to_device on the possibly-null pointer.
adam_prism_fnl_coil_object.F90:129-132 passes ierr=ierr four times and never tests it.
The same holds at every dev_alloc call site in the FNL backend.
So any device allocation failure becomes a null-pointer acc_memcpy_to_device — a SIGSEGV inside the assign, with no diagnostic. This is the mechanism that makes an out-of-memory condition present as "segfault in FUNDAL assign", and it is why the crash carries no useful information.
This defect is independent of Defect 1 and worth fixing regardless: it is the difference between a silent segfault and a labelled failure with a byte count.
Defect 3 — the dev_alloc at :132 is wasted work
initialize allocates j_vec_gpu (adam_prism_fnl_coil_object.F90:132) and immediately calls copy_cpu_gpu (:133), whose assign branch frees that fresh buffer and reallocates it. Not a leak — dev_assign_to_device frees before allocating — but it is a full-size device alloc+free of the largest coil array on every init, and the declared lbounds=[1,1-ngc,1-ngc,1-ngc,1,1] is silently discarded in favour of lbound(srct) from the transposed host array. If those bound sets ever disagree, the device layout is whatever the assign chose, not what :132 declared.
Proposed fix
Route the coil transfer through the buffered path, mirroring the q-field at adam_prism_fnl_object.F90:319:
Store db6/hb6 as type components next to the existing db5/hb5 (:306-309).
Pass buf6D from adam_prism_fnl_object.F90:328 and :349.
Drop the now-redundant dev_alloc at adam_prism_fnl_coil_object.F90:132, or keep it and drop the assign — not both.
This removes the per-call host allocation entirely and makes the transfer allocation-free after init.
Independently: check ierr after every dev_alloc in the FNL backend.
Also noted in this file
buf4D is declared in both copy_cpu_gpu (:41) and copy_gpu_cpu (:78) and referenced in neither body — dead parameter.
A_gpu/f_gpu/phase_gpu are allocated lbounds=[0], ubounds=[nc] (:129-131) and filled by unbounded dev_memcpy_to_device (:60-62). The host arrays are0:total_coils_number (adam_prism_coil_object.F90:130-132), so this is consistent — but by coincidence, not construction. Nothing asserts it.
Open question (decides priority vs the teardown issue)
Was the failing cluster case multi-realm? Per-realm device state is allocated N times and never freed (see the companion teardown issue), while initialize_prism:370 divides the device budget by realms_number. If the cluster manifest uses more realms than the local box, that is a second, independent path to the same symptom.
Adding the ierr check distinguishes the two immediately: Defect 1 fails in a hostallocate; the multi-realm path fails in acc_malloc with a reportable byte count.
Verification plan (when the cluster returns)
Add ierr checks to every FNL dev_alloc and re-run — converts the segfault into a labelled error in one run.
Print bytes and the host high-water mark immediately before the coil assign.
Symptom (reported, cluster-only)
FNL/OpenACC PRISM run segfaults inside FUNDAL
dev_assign_to_device, on the coil object's first host→device transfer. The fault appears only above a grid-size threshold, and that threshold is reported to be well below the VRAM limit. Not reproducible on the WSL dev box. No measurement was possible before the cluster went down, so the trigger below is derived from code inspection, not from a captured trace.The path that actually runs
prism_fnl_object%copy_cpu_gpucalls the coil copy withoutbuf6D:buf6Disoptionaland is never passed by any caller (:328,:349, andadam_prism_fnl_coil_object.F90:133). So the buffered branch atadam_prism_fnl_coil_object.F90:63-66is dead, and every coil transfer takes the fallback:which resolves to
DEV_ASSIGN_TO_DEVICE_KKP_6D_T(FUNDAL/src/lib/fundal_dev_assign_agnostic.INC:476-495):Defect 1 — transient host-memory doubling (suspected trigger)
Step 2 allocates a second full-size host copy of
j_vec. Between steps 2 and 4 the process holdsj_vecandsrctsimultaneously.j_vecis(3, 1-ngc:ni+ngc, 1-ngc:nj+ngc, 1-ngc:nk+ngc, nb, nc)(src/app/prism/common/adam_prism_coil_object.F90:134), so the transient scales as3·(ni+2ngc)·(nj+2ngc)·(nk+2ngc)·nb·nc·8bytes — a clean "grows with grid dimensions, crosses a threshold, dies" signature that is host RAM, not VRAM. That is consistent with the reported threshold sitting far below the VRAM limit.Why cluster-only is plausible: WSL is one rank against a large flat pool; a cluster node runs N ranks sharing node RAM, multiplying the same per-rank transient.
transpose_array_allocuses a plainallocatewith nostat=, so exhaustion aborts inside a FUNDAL routine bracketed by device calls — which reads as a device fault.This is a hypothesis consistent with the code and the reported symptom. It is not confirmed against a trace.
Defect 2 —
ierris discarded everywhere, converting OOM into SIGSEGVdev_alloc's failure path setsfptr_dev => null()and returnsFUNDAL_ERR_FPTR_DEV_NOT_ALLOCATED(fundal_dev_alloc_agnostic.INC:34-37). But:fundal_dev_assign_agnostic.INC:493capturesierrinto a local and never tests it, then unconditionally callsdev_memcpy_to_deviceon the possibly-null pointer.adam_prism_fnl_coil_object.F90:129-132passesierr=ierrfour times and never tests it.dev_alloccall site in the FNL backend.So any device allocation failure becomes a null-pointer
acc_memcpy_to_device— a SIGSEGV inside the assign, with no diagnostic. This is the mechanism that makes an out-of-memory condition present as "segfault in FUNDAL assign", and it is why the crash carries no useful information.This defect is independent of Defect 1 and worth fixing regardless: it is the difference between a silent segfault and a labelled failure with a byte count.
Defect 3 — the
dev_allocat:132is wasted workinitializeallocatesj_vec_gpu(adam_prism_fnl_coil_object.F90:132) and immediately callscopy_cpu_gpu(:133), whose assign branch frees that fresh buffer and reallocates it. Not a leak —dev_assign_to_devicefrees before allocating — but it is a full-size device alloc+free of the largest coil array on every init, and the declaredlbounds=[1,1-ngc,1-ngc,1-ngc,1,1]is silently discarded in favour oflbound(srct)from the transposed host array. If those bound sets ever disagree, the device layout is whatever the assign chose, not what:132declared.Proposed fix
Route the coil transfer through the buffered path, mirroring the q-field at
adam_prism_fnl_object.F90:319:buf_6D_R8Pinallocate_gpualongsidebuf_5D_R8P. It cannot reusebuf_5D_R8P— wrong rank and wrong extents; reusing a mismatched staging buffer is exactly the fWLayer failure mode already fixed under Inter-realm 1:1 mirror seam leaks div(B) on source-free pulse (same-resolution, refinement-divergent — distinct from #29) #31.db6/hb6as type components next to the existingdb5/hb5(:306-309).buf6Dfromadam_prism_fnl_object.F90:328and:349.dev_allocatadam_prism_fnl_coil_object.F90:132, or keep it and drop the assign — not both.This removes the per-call host allocation entirely and makes the transfer allocation-free after init.
Independently: check
ierrafter everydev_allocin the FNL backend.Also noted in this file
buf4Dis declared in bothcopy_cpu_gpu(:41) andcopy_gpu_cpu(:78) and referenced in neither body — dead parameter.A_gpu/f_gpu/phase_gpuare allocatedlbounds=[0], ubounds=[nc](:129-131) and filled by unboundeddev_memcpy_to_device(:60-62). The host arrays are0:total_coils_number(adam_prism_coil_object.F90:130-132), so this is consistent — but by coincidence, not construction. Nothing asserts it.Open question (decides priority vs the teardown issue)
Was the failing cluster case multi-realm? Per-realm device state is allocated N times and never freed (see the companion teardown issue), while
initialize_prism:370divides the device budget byrealms_number. If the cluster manifest uses more realms than the local box, that is a second, independent path to the same symptom.Adding the
ierrcheck distinguishes the two immediately: Defect 1 fails in a hostallocate; the multi-realm path fails inacc_mallocwith a reportable byte count.Verification plan (when the cluster returns)
ierrchecks to every FNLdev_allocand re-run — converts the segfault into a labelled error in one run.bytesand the host high-water mark immediately before the coil assign.compute-sanitizer. Per Inter-realm 1:1 mirror seam leaks div(B) on source-free pulse (same-resolution, refinement-divergent — distinct from #29) #31, the WSLfree/total memoryprint is/dev/dxggarbage and must not be used to rule out a device-side cause.