|
morton-arithmetic 0.1.0
Fast Morton (Z-order) codes with O(1) arithmetic
|
Two larger, separable future efforts. These are notes, not commitments — captured so the work can start without re-deriving the design.
The Hilbert curve has strictly better spatial locality than Morton/Z-order: consecutive indices are always face-adjacent (no "long jumps" across the domain), which improves cache/IO behaviour for range queries and tiling. Many spatial-index and HPC workloads prefer it. It is the natural second curve for a "space-filling-curve arithmetic" library.
uint_for/wide_uint machinery and the Dim*Bits ≤ MORTON_MAX_BITS framing are curve-agnostic — reuse as-is.iterate.hpp (probe → skip gap → resume) is the same shape.++/--, operator<, and the std::map-keying pattern transfer.PDEP/PEXT shortcut — it is inherently sequential over the bit levels, so expect it to be slower than Morton encode and not at parity with libmorton-style table encoders.A separate hilbert/ header (or a Curve policy template parameterising encode/decode/successor while sharing storage and range-search code). Keep Morton's arithmetic guarantees explicit so users don't assume Hilbert has the same O(1) neighbour step. Recommendation: ship Hilbert as encode/decode
Update: a portable Kokkos backend is now implemented in
include/morton/kokkos.hpp(morton::kokkos; it superseded the original raw-CUDA backend, retired at thepre-cuda-retirementtag), runs on CUDA / HIP / OpenMP, and follows the design below: the core is device-callable so kernels reuse the CPU code path, encode/decode use the software bit path, and the host API is array-shaped. Measured behaviour confirmed the prediction — device-resident throughput is ~33× one CPU core, but one-shot host calls are PCIe-transfer- bound. What remains from this section: the Z-order radix sort, the device-array Python entry point, and SYCL/HIP portability.
The bulk array ops in batch.hpp are embarrassingly parallel; large point sets (graphics, particle sims, databases) encode/sort/query millions of codes.
≤64-bit codes a thread handles one code; __uint128_t maps to a 2×u64 thread-local; wide_uint maps to a fixed u64[W].PDEP/PEXT have no GPU equivalent, so GPU encode/decode uses the software magic-bit/spread_sw path (already the constexpr fallback — share it).encode, decode, add/step (per-axis), and a sort (Z-order sort = radix sort on the code) which is the usual reason to go to GPU.bindings/ story extends: a peclet.morton.cuda submodule mirroring the CPU functions, dispatching on array device.batch.hpp path — for many workloads the bottleneck is the host↔device transfer, not the arithmetic (mirrors the CPU finding that bulk ops are already memory-bound). Measure before claiming a win.Both are candidates for separate optional packages depending on the morton core (as the octree now is), keeping the core small, dependency-free, and easy to audit.