Namespaces, suffixes, and flags#
GLASS names encode two orthogonal axes. Knowing which axis a given decoration lives on tells you what to expect from it.
Axis A — scope / backend (the namespace)#
The namespace says who cooperates and how, never what the operation is.
There are three execution scopes (block, warp, thread) and two implementation
families (dependency-free GLASS and optional NVIDIA, where supported), plus
glass::cgrps::, a cooperative-groups adapter for the Block interface, and the
bare glass:: face described below. Scope determines placement; the
family determines the implementation and dependency contract:
Namespace |
Scope |
What it is |
|---|---|---|
|
block |
Block — explicit hand-rolled pure-SIMT implementation ( |
|
warp |
Warp — single-warp SIMT ( |
|
thread |
Thread — sequential, thread-per-problem, for low-DOF packing (compile-time sizes; usually register-resident around |
|
block |
Nvidia — CUB / cuBLASDx / cuSOLVERDx, auto-dispatched by size at compile time. |
|
warp |
Nvidia-warp — CUB |
|
thread |
Nvidia-thread — cuSOLVERDx 0.4+ LAPACK, one packed problem per CUDA thread; smem-less signatures and no block-wide synchronization. |
|
block |
Measured default — block-scope calling contract, body chosen by |
|
block |
Adapter for callers that already hold a cooperative-groups handle (same numerics; not a separately-tuned backend). |
glass::thread:: mirrors the branch-free surface only: reduction strategy
twins (_fast / _lowmem), the contraction-parallel *_reduced family,
and the data-dependent/pivoted ops (iamax, pivoted ldlt, getrf,
syev) are deliberately absent — one thread has no reduction strategy to
choose, and with a different problem on every lane a data-dependent branch
diverges the whole warp. See the glass::thread:: constraints block in
CLAUDE.md for the full policy.
The convention is namespace = scope, function name = operation. So a warp
band-matvec is glass::warp::bdmv — never a glass::banded:: namespace.
Contract tier vs performance tier — the bare glass:: face#
The bare and explicit spellings make a deliberate implementation choice:
Explicit namespaces pin an implementation.
glass::block::gemm(and likewiseglass::warp::/glass::thread::/glass::nvidia::block::/glass::nvidia::warp::/glass::nvidia::thread::) is never re-dispatched. Use these from codegen and wherever implementation or reduction order is load-bearing.Bare
glass::gemmis the measured-default face: the same block-scope calling contract — all block threads enter, any thread count, the result is valid after return — with the implementation body chosen per (op, size, dtype) byglass::dispatch_body()inglass-dispatch.cuh. The choice is aconstexprselection made inside the device function — resolved per call site at compile time, not by a host-side dispatcher and not by any runtime branch.
The body sweep (bench/tune.py --legs body) compares full-block, warp-0, and
thread-0 bodies while preserving the one-problem-per-block calling contract.
Only cells meeting the configured robustness margin are moved; unmeasured
architectures and out-of-range sizes stay on the block body. A moved cell agrees
with the block result to its documented tolerance, not necessarily bit for bit.
Retuning is therefore a receipt-gated source change; see Tuning for Your Hardware.
Rule of thumb: explicit namespace = contract tier; bare namespace = performance tier.
There is intentionally no bare glass::nvidia::op re-export. NVIDIA calls
must name block, warp, or thread because that scope changes the
launch contract. This keeps glass::nvidia::* aligned with the explicit
native interfaces and prevents a block-only alias from looking like an
autotuned counterpart to bare glass::*.
Axis B — reduction strategy (function-name suffixes, vector reductions only)#
A second axis lives on the reduction primitives only: bare glass::reduce
(halving), glass::reduce_fast (warp-shuffle + shared inter-warp), and
glass::reduce_lowmem (thread-0 serial, no scratch) — performance/scratch
trade-offs of the same result. The same _fast / _lowmem suffixes
apply across the reduction family (dot, nrm2, asum, vector_norm,
nrm1_diff, iamax). This is a strategy, not a scope, so it rides on the
function name, never a sub-namespace — keeping namespace = scope true
everywhere. (These were glass::high_speed:: / glass::low_memory::
sub-namespaces until the 2026-06 convergence; that clean break is done.)
The naming rule for new code#
When you add an operation, decide what kind of variation it is:
A different algorithm or decomposition → its own function name (a suffix). The contraction-parallel gemm is
glass::gemm_reduced, not aglass::reduced::namespace — matching the existinggemm_tiled/gemm_dispatchprecedent. Same scope, different name.Optional, additive behavior → a compile-time
boolflag that compiles out.potrf<T, N, CHECK>,ldlt<T, N, CHECK>,posv<T, N, NRHS, REGULARIZE, CHECK>all default the flag tofalseand guard the extra work behindif constexpr— so the unflagged instantiation is byte-identical to the original (no PTX change, no perf cost). This is how the robustness features (non-PD detection, inertia, Levenberg shift) attach to the existing factor/solve ops instead of forking new functions.A different scope → a different namespace (Axis A), with the same function name.
So: scope picks the namespace, additive behavior picks a flag, and a genuinely different computation picks a new name. Following that keeps the surface predictable — you can guess the spelling of an op you have not seen.
As of the 2026-06 convergence, namespace means scope everywhere — the
former high_speed:: / low_memory:: reduction sub-namespaces are now the
_fast / _lowmem suffixes described above.