Neko 1.99.9
A portable framework for high-order spectral element flow simulations
Loading...
Searching...
No Matches
Appendices

The appendices contain a few extra pages that are not directly related to the usage of the code. But can be useful for users and developers alike.

Environmental variable reference

Name Description Default value
NEKO_AUTOTUNE Restrict the SEM operator search to one kernel formulation ('1D','KSTEP','DMMA','DMMA_TMA','DMMA_TMA_BATCH','MFMA') Unset
NEKO_EB_TUNE Sweep elements per block for the kstep kernels (boolean) 1
NEKO_EB Pin the kstep elements per block candidate (0, 1 or 2) Unset (swept)
NEKO_CHUNKS Pin the 1d chunk size candidate (0 to 3) Unset (swept)
NEKO_DMMA_NW Pin the DMMA warps per block candidate (0, 1 or 2) Unset (swept)
NEKO_DMMA_TMA_NW Pin the DMMA_TMA / DMMA_TMA_BATCH warps per block candidate (0, 1 or 2) Unset (swept)
NEKO_MFMA_NWF Pin the MFMA wavefronts per block candidate (0 to 4) Unset (swept)
NEKO_MFMA_TILE Pin the MFMA matrix core tile candidate (0 or 1) Unset (swept)
NEKO_MFMA_TUNE Sweep the matrix core variants on the HIP backend (boolean) 1
NEKO_TUNE_ROUNDS Interleaved sampling rounds used by the operator auto-tuner 3
NEKO_TUNE_ITERS Kernel launches timed per candidate per round 100
NEKO_LOG_FILE Log file name, uses stdout if not set. Unset
NEKO_LOG_TAB_SIZE Number of spaces added for each level of indentation in the log file. 1
NEKO_LOG_LEVEL Log verbosity level (integer > 0, default: 1) Unset
NEKO_GS_STRTGY Gather-scatter device MPI sync. strategy (0 < integer < 5 ) Unset
NEKO_GS_COMM Gather-scatter communication backend Unset
NEKO_GS_TUNE Comm. backends the gather-scatter autotuning benchmarks (list) Unset (all but CAF, NVSHMEM)
NEKO_GS_CAF_SIGNALING Coarray Fortran gather-scatter signaling mode Unset
NEKO_GS_RMA_FLUSH_ALL Batch the MPI RMA gather-scatter payload flush (boolean) 1
NEKO_COMM_ID Communicator id for this process (non-negative integer) 0
NEKO_HIP_ZEROCOPY Zero-copy host/device mapping on unified memory (HIP), 1 enables 0
NEKO_METAL_ZEROCOPY Zero-copy host/device mapping on unified memory (Metal), 0 disables 1
NEKO_MPI_THREAD_LEVEL Requested MPI (and SHMEM) thread support level Unset
NEKO_DEPRECATION_ERROR Whether to treat deprecated features as errors (boolean) Unset

SEM operator auto-tuning details

On the CUDA and HIP backends each spectral element operator benchmarks its available kernel formulations on first call and caches the winner for the rest of the run; see SEM operator auto-tuning for what is measured and why the defaults differ between vendors.

NEKO_AUTOTUNE narrows the search to one formulation. It does not decide the geometry within that formulation: the chunk sizes, elements per block or warps per block of the named variant are still measured against each other and reported, and it takes that variant's own selector (NEKO_CHUNKS, NEKO_EB, NEKO_DMMA_NW, NEKO_DMMA_TMA_NW, NEKO_MFMA_NWF) to fix the geometry as well. With both set there is nothing left to measure and the search is skipped outright.

  • NEKO_AUTOTUNE=1D : always use the 1d variant, sweeping its chunk sizes unless NEKO_CHUNKS pins one.
  • NEKO_AUTOTUNE=KSTEP : always use the kstep variant, sweeping its elements per block unless NEKO_EB pins one.
  • NEKO_AUTOTUNE=DMMA : always use the fp64 tensor core variant, sweeping its warps per block unless NEKO_DMMA_NW pins one. Available on CUDA for the Helmholtz operator Ax (scalar and vector), opgrad, dudxyz, conv1 and cdtp. Double precision only, 2 <= lx <= 8 except for the vector Ax, which is 4 <= lx <= 8, on an sm_80 or sm_90 device. convect_scalar and lambda2 have no such variant and keep tuning as usual.
  • NEKO_AUTOTUNE=DMMA_TMA : always use the TMA staged form of that variant, for every operator that has one, sweeping its warps per block unless NEKO_DMMA_TMA_NW pins one. Double precision and lx = 8 only, on an sm_90 device, and needs a CUDA 12 or later toolkit. The opgrad and conv1 forms stage past 48 kB and additionally need a device that will grant a block 54800 B and 71184 B respectively.
  • NEKO_AUTOTUNE=DMMA_TMA_BATCH : the batched form of the TMA variant, which stages all ten input cubes of an element at once. Same scope and same NEKO_DMMA_TMA_NW knob, but for the vector operator only — the scalar operator has no such variant and reports the value as invalid.
  • NEKO_AUTOTUNE=MFMA : the HIP counterpart of DMMA — always use the matrix core variant, sweeping its wavefronts per block — and, for the Helmholtz operator, its matrix core tile — unless NEKO_MFMA_NWF or NEKO_MFMA_TILE pins one. Available for the Helmholtz operator Ax (scalar and vector), opgrad, dudxyz, conv1 and cdtp. Either precision, 4 <= lx <= 12, on a gfx90a or gfx942 device.

The vector Helmholtz operator has no 1d formulation, so 1D and KSTEP both pin it to its kstep variant. It does have a matrix core variant on HIP, which MFMA pins as it does the scalar one.

Any other value is reported as an error and the search runs as usual. DMMA on a build or a device without fp64 tensor cores, or MFMA on one without matrix cores, or either at a polynomial order the variant does not cover, is reported the same way. So are DMMA_TMA and DMMA_TMA_BATCH on anything but an sm_90 device with a CUDA 12 toolkit, at any order but lx = 8, or when the field pointers the operator is handed are not 16 byte aligned, which is checked per tune rather than assumed; DMMA_TMA_BATCH, and the opgrad and conv1 forms of DMMA_TMA, additionally need a device that will grant a block the shared memory they stage into.

NEKO_EB_TUNE controls whether the elements per block dimension is swept at all, and defaults to enabled on both backends. It was once off on HIP, where the blocked kernels can exceed the register budget and spill to scratch, but fixing that verdict at build time also stopped the tuner from ever re-testing it. Where the blocked variants do spill the tuner rejects them, so the cost of sweeping is tuning time rather than run time. Set it to 0 to skip the sweep.

NEKO_MFMA_TUNE is the equivalent switch for the HIP matrix core variants. It defaults to enabled wherever the hardware and the polynomial order allow the variant at all; setting it to 0 keeps the strategy out of the sweep while leaving NEKO_AUTOTUNE=MFMA able to pin it explicitly.

NEKO_CHUNKS pins one of the instantiated chunk sizes, which the tuner would otherwise sweep. Candidates 0 to 3 are 1024, 512, 256 and 128 threads; candidate 0 is the historical value, so NEKO_AUTOTUNE=1D NEKO_CHUNKS=0 is the A/B baseline for the chunk sweep. A candidate smaller than one derivative matrix (lx*lx) is invalid and falls back to 1024. The variable applies whenever the 1d variant is measured, not only when it is the pinned one.

NEKO_EB pins one of the instantiated blocking candidates; candidate 0 is one element per block, i.e. the unblocked geometry, which makes NEKO_AUTOTUNE=KSTEP NEKO_EB=0 the A/B baseline for the blocking. Values outside the valid range fall back to 0.

NEKO_MFMA_TILE pins which matrix core tile the contraction is issued on, which the tuner otherwise sweeps: 0 is the precision's default tile and 1 the 16x16x4 one. It only names two different things in a double precision build — single precision has no 4x4x4 instruction, so both values are the 16x16x4 tile there, and the sweep measures the tile only where the two differ. The Helmholtz operator, scalar and vector, is the one that carries this dimension; opgrad, dudxyz, conv1 and cdtp keep their contractions on the default tile and ignore the variable.

Which tile is faster is not obvious and used to be fixed at build time. The batched 4x4x4 tile fills M = lx < 16 exactly where the 16x16x4 one wastes half its rows at lx = 8, but it runs at half the FLOP rate, owes four cycles on each step of an accumulate chain where the large tile owes none, and re-reads its second operand once per M-tile. Those cancel around lx = 8 and favour the large tile above it, so the tuner measures both rather than taking either argument on faith. Both are verified against a reference and give bit-identical double precision results, so the choice is a performance one only. The cost is tuning time: a double precision build times eight matrix core candidates per order instead of four.

NEKO_DMMA_NW pins one of the instantiated warps per block candidates of the tensor core variant; candidates 0, 1 and 2 are 2, 4 and 8 warps. Values outside the valid range fall back to 0. NEKO_DMMA_TMA_NW is the same selector for the two TMA staged variants, with the same three candidates, and is read by both DMMA_TMA and DMMA_TMA_BATCH. NEKO_MFMA_NWF is the HIP equivalent, with candidates 0 to 3 selecting 1, 2, 4 and 8 wavefronts per block.

On HIP that count also fixes the elements per block, and the two cannot be set independently: a contraction offers only ceil(lx*lx/16) column groups of wavefront-parallel work, so the block covers as many elements as that leaves wavefronts for — rounded down to a power of two, so that it divides nwf — and the rest cooperate on each. At lx = 8, where a contraction offers four column groups, the top candidate is two elements with four wavefronts each; at lx = 4, where it offers one, it is eight elements with one wavefront each.

NEKO_TUNE_ROUNDS and NEKO_TUNE_ITERS control the sampling, and apply to both sweeps — they are not specific to the elements per block dimension. Each round times every candidate once, chunk sizes included, and the best round per candidate is kept, so raising the round count guards against transients and clock drift during the sweep, whereas raising the iteration count only reduces per-sample noise.

Logging level details

A number of logging levels are supported.

  • NEKO_LOG_LEVEL=0 : Quiet mode, minimal logging during execution.
  • NEKO_LOG_LEVEL=1 : Default information mode, adding step informations.
  • NEKO_LOG_LEVEL=2 : Verbose mode, logging extra details.
  • NEKO_LOG_LEVEL=5 : Deprecated features will be logged if used.
  • NEKO_LOG_LEVEL=10 : Debug mode.

Gather-scatter communication backend details

A number of gather-scatter backends are supported.

  • NEKO_GS_COMM=MPI : Host based MPI
  • NEKO_GS_COMM=MPIGPU : Device based MPI
  • NEKO_GS_COMM=NCCL : NCCL/RCCL using its point-to-point interface
  • NEKO_GS_COMM=SHMEM : NVSHMEM based on GPU builds (NVIDIA GPUs); OpenSHMEM based on CPU builds (requires a native OpenSHMEM library, e.g. Cray OpenSHMEMX, enabled at configure time with --with-openshmem)
  • NEKO_GS_COMM=CAF : Coarray Fortran (requires a coarray-capable compiler)
  • NEKO_GS_COMM=NEIGHBOUR : Host MPI using an MPI_Ineighbor_alltoallv neighbourhood collective (NEIGHBOR is also accepted; MPI-3, host only)
  • NEKO_GS_COMM=UTOFU : Native Tofu interconnect (uTofu) one-sided RDMA (Fugaku and other Tofu-D systems; requires building with --with-utofu)
  • NEKO_GS_COMM=MPIRMA : Host MPI one-sided puts into a passive-target window (RMA is also accepted; MPI-3, host only). Assumes an MPI whose RMA progresses without the target entering MPI, as the hardware-driven one-sided components do (Open MPI osc/rdma and osc/ucx, Cray MPICH over libfabric); NEKO_GS_TUNE=-MPIRMA drops it from the autotuning on systems where that does not hold
  • NEKO_GS_COMM=CRYSTAL : Host MPI routed as a crystal router, one message per routing stage instead of one per peer (host only). CRYSTALGPU is the device-aware variant, which keeps the halo on the GPU

When NEKO_GS_COMM is unset, the backends are benchmarked at initialisation and the fastest one is kept (see Runtime autotuning of the comm. backend). Which ones take part is set by NEKO_GS_TUNE, a list of backend names spelled as for NEKO_GS_COMM (comma or space separated, case insensitive). Unset, it means every backend the build supports except CAF, which a compiler can accept at configure time while still giving the job a single image the coarray backend cannot use, and NVSHMEM, which aborts unless the peer lists come out symmetric and aligned. On a CUDA or HIP build the device-resident backends (MPIGPU, CRYSTALGPU and NCCL) are candidates alongside the host ones, which are then measured staging the halo through the host as they actually run there.

  • NEKO_GS_TUNE=+CAF adds the coarray backend to that default set, NEKO_GS_TUNE=-SHMEM removes a backend from it, and the two can be combined (NEKO_GS_TUNE=+CAF,-SHMEM).
  • Plain names replace the set outright: NEKO_GS_TUNE=MPI,NEIGHBOUR benchmarks those two and nothing else. A single name pins that backend without benchmarking anything.
  • The two forms cannot be mixed, and a name that is not a gather-scatter backend is an error. SHMEM here always names the host OpenSHMEM backend and NVSHMEM the device one, since a GPU build can have both as candidates.

Backends that are selected but cannot run in this build or run are dropped; those named explicitly are reported in the log as unavailable. Set NEKO_GS_COMM to skip the benchmark and pin a backend outright; that path ignores NEKO_GS_TUNE.

MPI RMA backend details

NEKO_GS_RMA_FLUSH_ALL selects how the MPI one-sided backend (NEKO_GS_COMM=MPIRMA) forces remote completion of the payload puts before announcing them. MPI has no put-with-notify, so the data and the signal are separate, unordered operations and the data must be flushed in between.

  • Unset or non-zero (the default): one MPI_Win_flush_all completes every put, then all the signals are issued.
  • NEKO_GS_RMA_FLUSH_ALL=0: each peer gets its own MPI_Win_flush and is announced as soon as its put lands, which releases early receivers sooner but costs one flush call per peer.

The per-peer form is the better structure wherever a flush really is scoped cheaply to the named target. On Cray MPICH over Slingshot it was not – the extra calls outweighed the pipelining they recovered – so the batched form is the default. It is worth re-measuring on an unfamiliar MPI; the setting is read once, at the first gather-scatter initialisation, so a run uses one strategy throughout.

MPI thread level details

NEKO_MPI_THREAD_LEVEL overrides the default thread support requested from the MPI runtime (and, when built with --with-openshmem, from the SHMEM runtime as well). When unset, the level is chosen automatically: MPI_THREAD_MULTIPLE when running with more than one OpenMP thread per rank, falling back to MPI_THREAD_FUNNELED on host-only backends, and MPI_Init otherwise. Device backends require MPI_THREAD_MULTIPLE under the automatic policy. Setting the variable bypasses these heuristics; if the runtime cannot honor the requested level, initialisation aborts.

  • NEKO_MPI_THREAD_LEVEL=single : MPI_THREAD_SINGLE (calls MPI_Init).
  • NEKO_MPI_THREAD_LEVEL=funneled : MPI_THREAD_FUNNELED.
  • NEKO_MPI_THREAD_LEVEL=serialized : MPI_THREAD_SERIALIZED.
  • NEKO_MPI_THREAD_LEVEL=multiple : MPI_THREAD_MULTIPLE.

Coarray Fortran signaling mode details

When the CAF gather-scatter backend is used, the per-pair synchronisation strategy is selected by NEKO_GS_CAF_SIGNALING. The mode is bound on the first gather-scatter initialisation and cannot change thereafter. The default (when unset) is sync.

  • NEKO_GS_CAF_SIGNALING=sync : sync images over the union of neighbour pairs, with a double-buffered receive coarray (F2008).
  • NEKO_GS_CAF_SIGNALING=atomic : Per-pair atomic counters via atomic_define/atomic_ref with a busy-wait spin (F2008).
  • NEKO_GS_CAF_SIGNALING=event : F2018 events (event post / event wait); requires a runtime that implements F2018 event semantics.
  • NEKO_GS_CAF_SIGNALING=auto : Benchmark the modes above that the build supports and bind the fastest one. Only takes effect when the comm. backend is autotuned too (i.e. NEKO_GS_COMM unset), and only on the first gather-scatter that tunes CAF, as the mode is shared by every instance; falls back to sync otherwise. See Runtime autotuning of the comm. backend.

uTofu injection details

When NEKO_GS_COMM=UTOFU, one injection virtual control queue (VCQ) is created per OpenMP thread, dealt round-robin over the Tofu network interfaces (TNIs) selected by NEKO_GS_UTOFU_NTNI (default 1). Every VCQ is created with UTOFU_VCQ_FLAG_THREAD_SAFE, and both the packing of the send buffer and the utofu_put calls are parallelised over the OpenMP team: each thread fires the puts for its share of the peers on its own VCQ. This is the uTofu substitute for the per-thread MPI_Isend that Fujitsu MPI's missing MPI_THREAD_MULTIPLE support rules out. The counts are bound on the first gather-scatter initialisation and cannot change thereafter; the TNI count is silently clamped to the number of one-sided TNIs the hardware exposes, and the VCQ count to whatever the TNIs' VCQ budget allows (threads beyond the granted VCQ count do not inject).

  • NEKO_GS_UTOFU_NTNI=1 (default) : All injection VCQs sit on one TNI.
  • NEKO_GS_UTOFU_NTNI=N : Deal the per-thread VCQs across N TNIs (benefits large messages and high neighbour counts).
  • NEKO_GS_UTOFU_NVCQ=N : Cap the injection pool at N VCQs. The VCQ budget (roughly 8 per TNI per rank on Fugaku) is shared with the per-instance receive VCQs, so on tight budgets the pool must leave room.
  • NEKO_GS_UTOFU_NRVCQ=N : Receive VCQs per instance path (default: one per TNI; 1 restores a single receive queue). Each receive peer is bound to one of them, so an instance's incoming halo traffic is processed by several TNIs instead of funnelling through one.
  • NEKO_GS_UTOFU_MASTER_INJECT=1 : Serialise all injection onto the master thread (still spread over every VCQ), as an A/B reference for the thread-parallel default.

The per-instance receive VCQs are placed round-robin over all one-sided TNIs (not only the NEKO_GS_UTOFU_NTNI injection set), sweeping past TNIs whose VCQ budget is exhausted; incoming puts are routed by the advertised VCQ id, so this costs nothing and balances receive processing across the interfaces. When the budget runs dry an instance is granted fewer receive VCQs (at least one).

The startup log prints the granted counts, e.g. uTofu inj. : 12 VCQs (12 threads) over 4 TNIs (6 requested).

The puts also request Tofu cache injection by default, which writes each arriving slab straight into the receiver's cache. This helps when the slab is consumed immediately but can pollute cache otherwise, so it can be disabled for A/B testing:

  • NEKO_GS_UTOFU_CACHE_INJECT=1 (default, or unset) : cache injection on.
  • NEKO_GS_UTOFU_CACHE_INJECT=0 : cache injection off.

The fused vector (multi-component) exchange can be disabled with NEKO_GS_UTOFU_VEC=0, in which case multi-component gather-scatter falls back to independent scalar rounds (a validation/bisection aid).