Skip to content

CPU network SoA: heap-free weights (index ABI, packing A) - #3826

Open
nrnhines wants to merge 34 commits into
masterfrom
local/cpu-net-soa-heap-free
Open

CPU network SoA: heap-free weights (index ABI, packing A)#3826
nrnhines wants to merge 34 commits into
masterfrom
local/cpu-net-soa-heap-free

Conversation

@nrnhines

@nrnhines nrnhines commented Jul 24, 2026

Copy link
Copy Markdown
Member

Summary

CPU network SoA heap-free path based on master: Weight SoA is sole storage for NetCon weights; sim-path identity is weight_index only.

  • No per-NetCon weight_ heap; no SelfEvent::weight_ pointer
  • O(1) NetCon shell + WeightBlock ownership; packing A sort
  • Fanout / FOR_NETCONS use indices (weight_bases + _nrn_fornetcon_weight)
  • pnt_receive / net_send / INITIAL: index ABI (int weight_index, −1 if none)
  • Generated MOD keeps _args[i] via _nrn_netrec_wsoa / _done (zero-copy when contiguous; TLS if scattered)
  • nocmodl + NMODL NEURON codegen aligned for that ABI

Docs: doc/network-soa/heap-free.md (steps 1–6, 6b–7c).

Includes earlier dual-write / SoA scaffolding from the local/cpu-network-soa line as intermediate history; the landable product story is heap-free on master, not dual-write alone.

Verification

  • build ctest 225/225
  • build-asan ctest 223/223
  • build-nmodl (NRN_ENABLE_NMODL=ON): build OK; network unit + network_soa_delivery + ringtests OK. Broader NMODL failures are pre-existing NMODL NEURON gaps (WATCH stub, FOR_NETCONS rename order, etc.), not heap-free ABI link failures.

Test plan

  • Full GHA CI green (PR base is master)
  • Smoke: NetCon delivery, net_send, SaveState/BBSaveState, external FOR_NETCONS (nocmodl path)
  • Optional: NMODL path only after NMODL WATCH/FOR_NETCONS completeness (out of scope here)

nrnhines added 30 commits July 8, 2026 18:00
Documents the decision to pause GPU network buffers until CPU network
SoA adoption. Provides agent rules, session starting prompt, and Phase 0
design checklist for local/cpu-network-soa branched from master.
Expand doc/network-soa-phase0.md into the authoritative field-tag, handle,
thread-slice, sidecar, and invalidation spec. Add neuron::container::network
PointProcess and Weight soa storages on Model (Node-style handles), wire
find_container_info/unsorted callbacks, and unit-test that handles survive
permutation.
Add §5.4.1 (target_/src_ as indices/handles, not data_handle) and
§5.5.1 (InputPreSyn deferred; thin gid→fanout after cpu-net-soa).
Embed PointProcess::owning_handle on Point_process and sync Instance/
MechType/ThreadId when prop or _vnt changes. NetCon allocates parallel
Weight SoA rows (heap weight_ stays delivery primary). Unit tests cover
dual-write helpers and PP shell lifetime.
Add network::NetCon storage (Target, WeightIndex, WeightCount, Delay,
Active, SrcPreSyn) on Model. NetCon owns an SoA row and syncs fields on
create/update. HOC weight() steers into Weight SoA data_handles; deliver
mirrors SoA↔heap around pnt_receive so NET_RECEIVE still sees double*.
Add network::PreSyn storage on Model and embed owning handles on PreSyn.
Rebuild a global NetCon* fanout order from dil_ (CoreNEURON-shaped ranges);
send/deliver/fanout iterate that order with dil_ fallback. NetCon::SrcPreSyn
points at the source PreSyn SoA row.
SelfEvent records Weight SoA base index and PointProcess row; net_send
resolves index from NetCon. Delivery uses nrn_pnt_receive_by_weight_index
(M2: SoA→heap→MOD pnt_receive→heap→SoA). NetCon::deliver shares that path.
…ght wipe

Document Phases 0–4 status and known gaps in GROK-NETWORK-SOA.md. Rebuild
PreSyn fanout at NetCvode::init_events (not only on first spike). Stop
soa_sync from copying heap weights over SoA (HOC weight writes). Add
pytest/ctest network_soa_delivery gate for NetStim→ExpSyn.
MOD/nrnivmodl builds only see build/include. Embedding PointProcess
owning_handle forced network/model_data headers that are not installed
there, breaking demo release compiles. Store a non-owning _soa_id on
Point_process; own the SoA row via a map in point.cpp. Add
point_process_access.hpp / nrn_point_process_soa_row for library code.
HOC weight[] is SoA-primary; pnt_receive_init and FOR_NETCONS mutate the
heap. Sync SoA↔heap around INITIAL, and for FOR_NETCONS targets sync all
NetCons sharing the target (including WatchCondition flag delivery).
Fixes hoctests::test_netrec_init_py and external_nrntest FOR_NETCONS/stdp1.
…eliver

Include PointProcess, Weight, NetCon, and PreSyn containers in
nrn_ensure_model_data_are_sorted (thread partition, weight-block repack,
fanout rebuild, cache offsets). On NetCon deliver without FOR_NETCONS,
pass a temporary buffer into pnt_receive instead of the long-lived weight_
heap so SoA stays the source of truth while the heap remains for SaveState
and FOR_NETCONS.
Save NetCon weights from Weight SoA (HOC-primary) and restore into both
heap and SoA so materialize-on-deliver matches SaveState. Resolve SelfEvent
ownership via NetCon object index and weight_index2netcon when the heap
pointer is absent; index2netcon maps all NetCons by HOC index.
Materialize Weight SoA into the heap before BBSaveState weight IO and
mirror heap back to SoA after restore. Match SelfEvents to target NetCons
by heap base or weight_index, and rebind both fields from the DEList
ncindex on restore (same dual-write policy as SaveState).
sort_network_data must resolve HOC point-process objects via
ob2pntproc_0 (dataspace), not this_pointer. NetCon deliver must pass the
long-lived weight_ heap into pnt_receive so MOD net_send(..., _w)
preserves SelfEvent identity for SaveState; resolve owner by weight_index
when needed. Softens SelfEvent savestate_write when no NetCon is found.
Add doc/network-soa/ with L0 README, topology (fanout authority, CoreNEURON
mapping), dual-write/heap roadmap, and sort packing. Point GROK handoff at
the layered map and correct stale short-lived-materialize status text.
Sync Weight SoA when exporting NetCon weights to CoreNEURON and when
returning weights after a CoreNEURON run (HOC weight[] is SoA-primary).
Apply black/clang-format line-wrap fixes for network SoA tests. Merge
with master already on branch for formatting toolchain updates.
SelfEvent from mech INITIAL often has nullptr weight; still route through
nrn_pnt_receive_by_weight_index so FOR_NETCONS heap mutations sync to SoA.
Apply ninja format-pr clang-format fixes for network SoA sources.
ASan CI: nrn_point_process_soa_sync during free_one_point/relocate read
freed Prop or dangling state. Stop syncing on free (SoA row released in
~Point_process); harden sync to verify prop still owns this pnt and that
_vnt is a live NrnThread.

NetCon factory wrote magnitude only to weight_ heap; HOC weight[] reads
SoA and init_events soa_to_heap wiped the value (0.0 != 0.1). Mirror
magnitude into Weight SoA after construction.
Do not invent a short-lived weight buffer when weight_index < 0
(flag-only / INITIAL net_send); MOD may net_send that pointer and
corrupt later queue / BBSaveState handling. Skip NET_RECEIVE INITIAL
for NetCons whose target Prop is already freed.
Branch-only follow-on to dual-write PR tip. Record settled heap-free
policy (O(1) weight block, packing A, queue clear on structure change,
indices in bulk tables). Add weight_index_t/netcon_index_t and wire
NetCon SoA WeightIndex/WeightCount to them.
Replace in-shell vector of Weight owning handles with an off-shell
WeightBlock (unique_ptr). NetCon SoA WeightIndex/WeightCount are the
authority for the base; HOC weight[i] uses a stable data_handle from
the block. Dual-write weight_ heap remains for MOD until a later step.

sizeof(NetCon) 104 -> 88 on this platform.
Replace g_network_fanout_order vector<NetCon*> with vector<netcon_index_t>
(SoA rows). Rebuild g_netcon_by_soa_row for O(1) shell resolve on deliver.
PreSyn NcIndex/NcCount use the compact index types. dil_ remains the
rebuild source and mid-update fallback. Unit tests cover index width and
resolve model.
nocmodl emits walks over weight bases via _nrn_netcon_weight_bases and
_nrn_fornetcon_weight (resolve base to dual-write weight_ heap). ForNetConsInfo
stores both bases and legacy double** argslist. Sort packs NetCon/weights by
target PP SoA row after thread (packing A) for peer locality.
Non-FOR_NETCONS pnt_receive materializes Weight SoA into a thread-local
buffer and writes back after MOD. TLS active weight_index binds net_send
SelfEvents so scratch pointers are never queued. FOR_NETCONS still uses
long-lived weight_ heaps until step 6. nrn_net_send only retains weight_
when it is a known NetCon heap base.
Weight SoA is sole storage for edge weights. Non-FOR_NETCONS deliver uses
thread-local scratch; FOR_NETCONS owns per-target scratch buffers keyed by
weight bases. weight_soa_data() exposes contiguous SoA doubles for legacy
APIs (BBSS, CoreNEURON export, nrn_netcon_weight). SelfEvent identity is
weight_index only. sizeof(NetCon) 80 on this platform.
Capture sim vs edit epochs: CoreNEURON-shaped hot path plus packing A and
reconfigurable nthread; full NEURON construction between runs. Mark steps
1–6 done; outline 6b zero-copy and 7 nocmodl index ABI as the next phase.
Prefer data_if_contiguous() / weight_soa_data() for non-FOR_NETCONS deliver
and NET_RECEIVE INITIAL so MOD writes SoA in place. TLS materialize remains
only when the block is scattered (post-erase, pre-sort). SelfEvent identity
stays weight_index. Unit test covers contiguous zero-copy writes.
Change pnt_receive_t and pnt_receive_init_t to
(Point_process*, int weight_index, double flag). Generated NET_RECEIVE
(nocmodl + NMODL neuron) keeps _args[i] via _nrn_netrec_wsoa /
_nrn_netrec_wsoa_done; net_send / artcell_net_send take the SoA base
index (−1 if none). Always commit wsoa_done so NET_RECEIVE INITIAL
values reach Weight SoA when fornetcon_prepare has already mapped
FOR_NETCONS scratch.

Verified: build 225/225, build-asan 223/223.
ForNetConsInfo now holds only Weight SoA base indices. Generated
FOR_NETCONS resolves each peer via _nrn_fornetcon_weight: zero-copy
into a contiguous SoA block when packed, otherwise a shared TLS view
committed on the next peer or at receive end. Removes argslist,
weight_storage, and the base→buf map; receive path no longer bulk-
syncs a per-target double pool.

Verified: build 225/225, build-asan 223/223.
nrnhines added 2 commits July 24, 2026 12:27
Remove SelfEvent::weight_ (queue identity is weight_index only) and
_nrn_netcon_args (bases + _nrn_fornetcon_weight only). TLS commit on
_nrn_netrec_wsoa_done only when the primary edge used materialize;
zero-copy SoA needs no writeback. Document post-7a–7c host path shape.

Verified: build 225/225, build-asan 223/223.
@azure-pipelines

Copy link
Copy Markdown

✔️ 9eebc43 -> Azure artifacts URL

@nrnhines
nrnhines changed the base branch from local/cpu-network-soa to master July 25, 2026 00:16
@azure-pipelines

Copy link
Copy Markdown

✔️ 9eebc43 -> Azure artifacts URL

nrnhines added a commit that referenced this pull request Jul 25, 2026
Point handoff at local/gpu-native-net-soa (PR #3826 base + gpu-native
merge). Record baseline parity @ 0.025/1.0, unpause Stages 2–3 with
weight_index ABI constraints, and replace obsolete post_solve open-bug
starting prompt.
nrnhines added 2 commits July 25, 2026 19:21
Sim path was looking up NetCon* from weight_index on every NET_RECEIVE
(_nrn_netrec_wsoa / FOR_NETCONS), which is CoreNEURON-unlike and O(N) over
HOC NetCons — Traub 1/10 hung after first spikes until nrn_timeout.

Address model().weights()[base] directly (weight_soa_ptr), TLS materialize
only if out of range. Remove weight_index2netcon; NetCon→base stays
forward-only (owning handles / WeightIndex on sort). SaveState SelfEvent
write uses a cold O(N) scan for HOC object index only.

Verified: Traub one_tenth no-gap, nthread=1, pc.timeout(1); 100 ms
nocmodl and NMODL both complete with exact 4474 spikes.
Classic nrnivmodl did not export NMODL_PYLIB or NMODLHOME, so nmodl
aborted when NRN_ENABLE_NMODL is ON (or -nmodl). Set NMODL_PYLIB from
configure-time PYTHON_LIBRARY and NMODLHOME from the install prefix.
Harmless with nocmodl.
@sonarqubecloud

Copy link
Copy Markdown

WHEN("A random reverse-permutation is applied") {
std::vector<std::size_t> perm(n);
std::iota(perm.begin(), perm.end(), 0);
std::mt19937 g{42};
std::vector<std::size_t> perm(n);
std::iota(perm.begin(), perm.end(), 0);
std::mt19937 g{42};
std::shuffle(perm.begin(), perm.end(), g);
@github-actions

Copy link
Copy Markdown
Contributor

✔️ a49fbad -> artifacts URL

@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.41927% with 137 lines in your changes missing coverage. Please review.
✅ Project coverage is 68.66%. Comparing base (0c9dab0) to head (a49fbad).
⚠️ Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
src/nrncvode/netcvode.cpp 75.34% 71 Missing ⚠️
src/nrncvode/network_soa_sort.cpp 94.24% 11 Missing ⚠️
src/nrniv/bbsavestate.cpp 74.28% 9 Missing ⚠️
src/nrnoc/container.cpp 61.90% 8 Missing ⚠️
src/neuron/container/network/weight_block.hpp 85.10% 7 Missing ⚠️
test/unit_tests/container/network.cpp 98.10% 6 Missing ⚠️
src/neuron/container/memory_usage.hpp 66.66% 4 Missing ⚠️
src/neuron/container/network/netcon.hpp 85.71% 4 Missing ⚠️
src/neuron/container/network/self_event.hpp 85.71% 4 Missing ⚠️
src/nrnoc/point.cpp 90.90% 3 Missing ⚠️
... and 6 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3826      +/-   ##
==========================================
+ Coverage   68.48%   68.66%   +0.18%     
==========================================
  Files         691      700       +9     
  Lines      111413   112493    +1080     
==========================================
+ Hits        76301    77244     +943     
- Misses      35112    35249     +137     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

nrnhines added a commit that referenced this pull request Jul 26, 2026
NMODL-generated Traub mechs failed to load on NRN_ENABLE_GPU installs with
undefined mangled hoc_register_net_send_buffering: GPU defs are extern "C"
but public headers declared C++ linkage. Align declarations and CPU stubs.

Also refresh GROK-GPU-NATIVE for #3826 absorb, ringtest re-green, and Traub
NMODL CPU smoke (4474 spikes @ 100) on this install.
@azure-pipelines

Copy link
Copy Markdown

✔️ a49fbad -> Azure artifacts URL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants