Skip to content

Commit 9874a5b

Browse files
committed
Document native GPU gap junction CPU fixed-step fallback
Phase B docs previously described a hybrid GPU solve with host partrans gather/scatter. When nrnthread_v_transfer_ is registered, fadvance.cpp dispatches the full CPU fixed-step body instead of fadvance_gpu.cpp. Update the Sphinx scope contract, design journal, limitations, future work, test parity notes, and Phase B checklist to match.
1 parent 39dd87d commit 9874a5b

8 files changed

Lines changed: 82 additions & 38 deletions

docs/dev/native-gpu-adoption/00-overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ See [04-known-limitations.md](04-known-limitations.md) and
2424
- GPU spike priority queue
2525
- Standalone `-DNRN_ENABLE_GPU=ON` configure without `-DNRN_ENABLE_CORENEURON=ON`
2626
- Full multi-thread OpenACC maturity
27+
- Device partrans gather/scatter (gap models use CPU fixed-step fallback today)
2728
- MPI-native modtest expansion beyond the current parity set
2829

2930
## Stopping point

docs/dev/native-gpu-adoption/01-design-decisions.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,26 @@ after topology changes must invalidate cleanly.
4646

4747
**Artifacts:** `device_state.cpp`, `upload.cpp`.
4848

49-
## Gap junctions: three-phase partrans, host staging in Phase B
49+
## Gap junctions: CPU fixed-step fallback in Phase B
5050

51-
**Decision:** Gap transfer follows CPU partrans phases when
52-
`pc.setup_transfer()` is active (not only when MPI or `nthread>1`):
51+
**Decision:** When `pc.setup_transfer()` registers `nrnthread_v_transfer_`,
52+
native GPU fixed-step dispatches the **full CPU body** in `nrn_fixed_step_thread`
53+
(`fadvance.cpp`) instead of `fadvance_gpu.cpp`. Partrans then runs unchanged on
54+
the CPU path:
5355

5456
1. Gather sources into buffers (`mpi_transfer` / `outsrc_buf_`)
5557
2. `MPI_Alltoallv` when `nrnmpi_numprocs > 1` (skipped on one rank)
5658
3. Scatter to targets (`thread_transfer` in `nonvint`)
5759

58-
Native GPU pulls source voltages device → host (`sync_gap_after_voltage_update`)
59-
before gather so `mpi_transfer` reads host memory. Scatter remains CPU in lastpart.
60+
`gpu.enable=True` still uploads model state; integration per step is CPU until
61+
device-resident gather/scatter is implemented.
6062

61-
**Why:** Partrans predates device-resident gap buffers. GPU gather/scatter without
62-
host staging — especially when MPI is absent — is a future optimization, not
63-
Phase B.
63+
**Why:** A hybrid GPU solve plus host partrans gather/scatter diverged on Traub
64+
(`use_gap=1`) and could overwrite host voltages with stale device state. Full CPU
65+
fallback restores raster parity (`test_par_gj_native_gpu.py`, ringtest `-gap`) at
66+
~1× NEURON CPU cost. Device partrans buffers are follow-up work, not Phase B.
6467

65-
**Artifacts:** `partrans.cpp`, `sync.cpp`, `fadvance_gpu.cpp`.
68+
**Artifacts:** `fadvance.cpp`, `partrans.cpp`, `sync.cpp`, `fadvance_gpu.cpp`.
6669

6770
## Batch download for recording
6871

docs/dev/native-gpu-adoption/02-implementation-map.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ repo root.
5454
| Artifact | Role |
5555
|----------|------|
5656
| `src/neuron/gpu/post_solve.cpp` | `post_solve_needs_host_fallback()` |
57-
| `src/nrnoc/fadvance.cpp` | CPU `nrn_update_voltage` when fallback required |
57+
| `src/nrnoc/fadvance.cpp` | CPU `nrn_update_voltage` when fallback required; **gap CPU fixed-step fallback** when `nrnthread_v_transfer_` is set |
5858

5959
## L6 — Communication
6060

@@ -64,8 +64,8 @@ repo root.
6464
| `src/neuron/gpu/net_events.hpp` | Spike / `NET_RECEIVE` policy comments |
6565
| `src/neuron/gpu/net_send_buffer.cpp` | GPU `net_send` buffering |
6666
| `src/neuron/gpu/net_send_buffer.hpp` | Buffer semantics |
67-
| `src/neuron/gpu/sync.cpp` | Gap / voltage device↔host sync |
68-
| `src/nrniv/partrans.cpp` | MPI gap transfer (host) |
67+
| `src/neuron/gpu/sync.cpp` | Gap voltage device↔host sync (hybrid path; unused on Phase B CPU fallback) |
68+
| `src/nrniv/partrans.cpp` | Partrans gather / MPI / scatter (CPU) |
6969

7070
## L7 — Recording
7171

docs/dev/native-gpu-adoption/03-test-parity.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ tests without running the full CoreNEURON GPU matrix.
6363

6464
Filter: `ctest -R testneuron_gpu`
6565

66+
## Gap junction supplementary test
67+
68+
Not part of the 19-ctest parity set; run manually after building with GPU:
69+
70+
```console
71+
cd test/gjtests && nrnivmodl
72+
python3 test_par_gj_native_gpu.py
73+
```
74+
75+
Compares `test_par_gj` dendrite voltages CPU vs `gpu.backend=native`. Ringtest
76+
`-gap -gpu-native` (single rank) is an additional raster check; CTest
77+
`neuron_gpu_native_mpi` does not pass `-gap` today.
78+
6679
## Ringtest native benchmark
6780

6881
External ringtest supports `-gpu-native` (orthogonal to CoreNEURON `-gpu`).

docs/dev/native-gpu-adoption/04-known-limitations.md

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,25 @@ not ruled out by the architecture, but Phase B does not certify DAE models on
3131
`gpu.backend="native"`. `post_solve_needs_host_fallback()` often forces CPU
3232
post-solve for DAE/LFP cases as well.
3333

34-
## LFP / partrans post-solve fallback
34+
## Gap junctions: CPU fixed-step fallback
3535

36-
When `nrnthread_vi_compute_` is registered (gap/partrans v+vext interpolation),
37-
post-solve runs on the host via `nrn_update_voltage` instead of
38-
`post_solve_on_device`. The matrix solve may still be on GPU for type-1 ODE models.
36+
When `pc.setup_transfer()` is active (`nrnthread_v_transfer_` registered), native
37+
GPU does **not** run `fadvance_gpu.cpp` for fixed-step integration. The CPU
38+
`nrn_fixed_step_thread` body handles matrix setup, solve, post-solve, partrans
39+
gather/MPI/scatter, and lastpart. Correctness is validated (e.g.
40+
`test/gjtests/test_par_gj_native_gpu.py`); performance matches NEURON CPU (~1×),
41+
not the GPU-accelerated non-gap path.
42+
43+
Device-resident partrans gather/scatter — restoring GPU integration subphases while
44+
keeping host/MPI only for cross-rank `MPI_Alltoallv` — is deferred (see
45+
[05-future-work.md](05-future-work.md)).
46+
47+
## LFP / extracellular post-solve fallback
48+
49+
When `nrnthread_vi_compute_` is registered (extracellular `v+vext` sources for
50+
partrans), `post_solve_needs_host_fallback()` forces host `nrn_update_voltage`
51+
instead of `post_solve_on_device` on the **GPU integration path**. This applies
52+
only when the native GPU step runs (no `nrnthread_v_transfer_` CPU fallback).
3953

4054
## MPI-native modtest gap
4155

docs/dev/native-gpu-adoption/05-future-work.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Phase B checklist.
1212
| GPU spike priority queue | Requires MPI + device ordering design |
1313
| Full multi-thread OpenACC | Harden per-thread CUDA contexts; remove warning |
1414
| `sparse13` / extracellular / LFP on device | Eliminate per-step host fallbacks |
15+
| Device partrans gather/scatter | Remove gap CPU fixed-step fallback; host/MPI only when `nrnmpi_numprocs > 1` |
1516
| MPI-native modtest expansion | Beyond 19-test single-process parity |
1617
| Per-step matrix host sync elimination | Performance optimization in `treeset.cpp` |
1718

docs/dev/native-gpu-adoption/PHASE-B-COMPLETE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Checklist for closing native GPU **fixed-step** Phase B on branch
88
- [x] Fixed-step `pc.psolve` on `gpu.backend=native` without `coreneuron.enable`
99
- [x] OpenACC matrix solve + mechanism state + post-solve on device
1010
- [x] CPU spike queues + MPI; GPU `NET_RECEIVE` and `net_send` buffer
11-
- [x] Gap junctions with device voltage sync before MPI
11+
- [x] Gap junctions: CPU fixed-step fallback when `setup_transfer` active (correctness; `test_par_gj_native_gpu`)
1212
- [x] Batch download API (`download_flush_interval`)
1313
- [x] CVode rejected when native GPU enabled
1414
- [x] Threading warning for `pc.nthread(n>1)`

docs/dev/native-gpu-fixed-step.rst

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ Phase B contract
4444
- ``NET_RECEIVE`` mechanism computation on GPU during the step
4545
- CPU spike priority queues + MPI exchange at the minimum NetCon delay interval
4646
- GPU ``net_send`` buffering; cross-cell events to CPU/MPI; self-events may flush on device
47-
- Gap junctions: device voltage pull, sparse MPI transfer, host fallback where required
47+
- Gap junctions: **correctness** on native GPU when ``pc.setup_transfer()`` is
48+
active; fixed-step integration runs the **CPU body** (full fallback) until
49+
device partrans gather/scatter is implemented
4850
- Batch host download API for ``Vector.record`` and graphs (``download_flush_interval``)
4951

5052
**Out of scope (deferred)**
@@ -76,7 +78,7 @@ Supported and unsupported matrix
7678
+-------------------------------+------------------+---------------------------+
7779
| ``net_send`` self-events | GPU buffer | May flush on device |
7880
+-------------------------------+------------------+---------------------------+
79-
| Gap junctions | Supported | Sparse MPI voltage/ion |
81+
| Gap junctions | Supported | CPU fixed-step fallback |
8082
+-------------------------------+------------------+---------------------------+
8183
| ``Vector.record`` | Supported | Batch download API |
8284
+-------------------------------+------------------+---------------------------+
@@ -154,8 +156,8 @@ not a rewrite of the whole native path.
154156
+---------------------------+--------------------+-------------------------------+
155157
| ``net_send`` buffer | GPU | GPU-capable in principle |
156158
+---------------------------+--------------------+-------------------------------+
157-
| Gap junction transfer | See gap phases | Same (if gaps present) |
158-
| (``setup_transfer``) | below | |
159+
| Gap junction transfer | **CPU fixed-step | Same (if gaps present) |
160+
| (``setup_transfer``) | fallback** (below) | |
159161
+---------------------------+--------------------+-------------------------------+
160162
| Recording flush | GPU → host batch | Same API |
161163
+---------------------------+--------------------+-------------------------------+
@@ -176,21 +178,22 @@ limitations (see matrix above).
176178
.. mermaid::
177179

178180
flowchart TD
179-
A[CPU: deliver_net_events] --> B[GPU: setup_tree_matrix + NMODL currents]
181+
S{nrnthread_v_transfer_ registered?}
182+
S -->|yes| CPUALL[CPU fixed-step: deliver, matrix, solve, update, gap transfer, lastpart]
183+
S -->|no| A[CPU: deliver_net_events]
184+
A --> B[GPU: setup_tree_matrix + NMODL currents]
180185
B --> C[GPU: tree solve solve_interleaved]
181186
C --> D{post_solve host fallback?}
182187
D -->|no| E[GPU: post_solve V, fast_imem, capacity]
183188
D -->|yes| F[CPU: post_solve nrn_update_voltage]
184-
E --> G{gap transfer configured?}
185-
F --> G
186-
G -->|yes| H[CPU: gap gather, MPI if nhost>1, scatter in lastpart]
187-
G -->|no| I[GPU: lastpart NET_RECEIVE + nonvint + vecplay]
188-
H --> I
189+
E --> I[GPU: lastpart NET_RECEIVE + nonvint + vecplay]
190+
F --> I
189191
I --> J{download flush interval?}
190192
J -->|yes| K[batch_download_to_host]
191193
J -->|no| L[defer to next flush / psolve end]
192194
K --> M[advance step counter]
193195
L --> M
196+
CPUALL --> M
194197

195198
On this path ``nt.compute_gpu`` is set for integration. NMODL ``BREAKPOINT``
196199
currents (``nrn_cur_*`` OpenACC) and OpenACC axial assembly in ``setup_tree_matrix``
@@ -202,12 +205,22 @@ run on the GPU. **GPU tree solve** is ``solve_interleaved`` / ``solve_interleave
202205
during **lastpart** (after the solve/post-solve block). Cross-rank spike
203206
**scheduling** remains on CPU (see spike policy in :doc:`gpu-testing`).
204207

205-
**Gap junction transfer** (``ParallelContext.setup_transfer``) is **not** gated on
206-
MPI or ``pc.nthread(n)>1`` alone. The diamond is true whenever
207-
``nrnthread_v_transfer_`` is registered (gap junctions or other partrans targets) —
208-
including single-process, single-thread models with gaps.
208+
**Gap junction transfer** (``ParallelContext.setup_transfer``) registers
209+
``nrnthread_v_transfer_`` whenever partrans targets exist — including
210+
single-process, single-thread models with gaps (not only when MPI or
211+
``pc.nthread(n)>1``).
209212

210-
On CPU fixed-step, partrans uses three conceptual phases (``partrans.cpp``):
213+
Phase B native GPU **does not** run the hybrid GPU integration path above when
214+
``nrnthread_v_transfer_`` is set. ``nrn_fixed_step_thread`` in ``fadvance.cpp``
215+
dispatches the **full CPU fixed-step body** instead of ``fadvance_gpu.cpp`` until
216+
device-resident partrans gather/scatter is complete. ``gpu.enable=True`` still
217+
uploads mechanisms and state to device, but matrix setup, solve, post-solve, gap
218+
transfer, and lastpart execute on the CPU for correctness (validated by
219+
``test/gjtests/test_par_gj_native_gpu.py`` and ringtest ``-gap``). Runtime is
220+
therefore ~1× NEURON CPU for gap models, not the slower hybrid path seen on
221+
non-gap workloads.
222+
223+
On that CPU path, partrans uses three phases (``partrans.cpp``):
211224

212225
1. **Gather** source values into transfer buffers (``mpi_transfer``: ``outsrc_buf_[i]
213226
= *poutsrc_[i]``).
@@ -216,12 +229,11 @@ On CPU fixed-step, partrans uses three conceptual phases (``partrans.cpp``):
216229
3. **Scatter** to targets (``thread_transfer`` in ``nonvint``, per thread:
217230
``*(ttd.tv[i]) = *(ttd.sv[i])``).
218231

219-
Phase B native GPU: after post-solve, ``sync_gap_after_voltage_update`` pulls source
220-
voltages **GPU → host** so phase 1 can read them; thread 0 runs ``nrnmpi_v_transfer_``
221-
(MPI phase 2 when multi-rank); phase 3 still runs on the **CPU** inside lastpart
222-
``nonvint``. Gather and scatter are not on device yet — a future optimization could
223-
keep phases 1 and 3 on GPU and avoid host staging when **no MPI** (and possibly for
224-
intra-node thread transfers), but that is outside Phase B.
232+
A future optimization would restore the GPU integration subphases and move phases
233+
1 and 3 to device buffers, touching host/MPI only when ``nrnmpi_numprocs > 1``.
234+
The hybrid sync helpers in ``sync.cpp`` (``sync_gap_after_voltage_update``,
235+
``sync_gap_after_host_voltage_update``) remain for that path; they are not used on
236+
the Phase B CPU fallback dispatch.
225237

226238
For DAE models, only the **matrix solve** subphase must swap to CPU ``sparse13``;
227239
other subphases are not inherently excluded by the subphase design, but Phase B

0 commit comments

Comments
 (0)