/ concept-collection / turing-sphere
Sign in
concept-collection / turing-sphere
8.2 KBPreviewCodeBlameHistoryRaw

The WGSL transforms against upstream SHTNS#

src/sht/ is a WGSL translation of SHTNS, vendored from shtns-webgpu. Its tests check it against its own f64 CPU twin, which says it is self-consistent — not how it compares with the library it is modeled on.

This directory answers that. It builds upstream SHTNS and runs the same spec through it, so the numbers line up against npm run bench:

transforms precision where
npm run bench:sht WGSL, via Dawn fp32 this repo
./shtbench_gpu SHTNS' own CUDA kernels fp32 upstream
./shtbench SHTNS on the CPU fp64 upstream

shtbench_gpu is the like-for-like comparison: same GPU, same precision, same grid, same spectral conventions — the only difference is who computes the transform. shtbench is fp64 because SHTNS' single precision exists only on the GPU, so it serves as the accuracy reference (how far has fp32 drifted?) and as the "what does a well-optimized CPU do" reference.

Build#

./bootstrap.sh          # clone SHTNS at a pinned commit, configure, build
make                    # shtbench, plus shtbench_gpu if SHTNS got CUDA support

bootstrap.sh adds --enable-cuda when nvcc is on PATH and passes --enable-openmp always; --no-cuda and --cuda=ampere override it. It writes shtns.mk with the library name and the link flags configure decided on, which the Makefile includes — so a host that needed MKL or a different FFTW still links without editing anything here. Both the checkout and shtns.mk are gitignored; make distclean throws them away.

Needs: a C++ compiler, FFTW3 headers (libfftw3-dev), and for the GPU half a CUDA toolkit. SHTNS' configure wants CUDA_PATH set; bootstrap.sh derives it from nvcc's location if it is not.

Run#

./shtbench_gpu --mode transform --lmax 63 --steps 2000
./shtbench_gpu --mode solver    --lmax 63 --steps 2000 --preset schnak-spots
./shtbench --help

Both binaries take the same options as npm run bench--preset, --lmax, --steps, --warmup, --seed, --batch, any model parameter by name — plus --mode, --layout, --polar-eps, --json, --digest, --dump-state.

Two things are measured, and they answer different questions:

Both report throughput (a batch launched together, waited for once — what --batch controls, matching npm run bench -- --batch) and a per-step distribution from one synchronization per step.

Compare#

node scripts/compare-native.mjs                  # transforms, lmax 63
node scripts/compare-native.mjs --mode solver
node scripts/compare-native.mjs --check          # and diff the final state

from the repo root (or npm run bench:native --). It runs every implementation present on the machine, back to back in one invocation so a second process competing for the GPU affects both sides rather than one, and prints them in one table. Missing implementations are reported and skipped, so this is still useful on a machine with no CUDA.

--check adds a short second pass that diffs the final spectral state across implementations. That is what makes the timing mean anything: two numbers are only comparable if they are the cost of the same computation.

What is and is not the same on the two sides#

The comparison is exact where it can be:

And explicit where it cannot be:

Reading the result#

Small grids flatter the CPU: at lmax 31 there are 528 coefficients, and a GPU spends most of a transform on launch latency rather than arithmetic. The gap closes with lmax, so run a sweep before concluding anything:

for l in 31 63 127 255; do node scripts/compare-native.mjs --lmax $l --steps 500; done

If the WGSL side lands on a software adapter, compare-native.mjs says so and stops you — the ratio then compares a CPU emulation against a real GPU and means nothing.

Editing shtbench_gpu.cu without a GPU#

nvcc is needed to build it, but not to typecheck it. Rewriting the launch syntax as calls, against a handful of stub declarations, gets g++ to check everything else:

sed -e 's/<<</\/*/g; s/>>>/*\//g' shtbench_gpu.cu > /tmp/check.cpp
g++ -fsyntax-only -std=c++14 -I. -Ishtns -I/path/to/cuda-stubs /tmp/check.cpp

where the stub directory holds a cuda_runtime.h defining __global__, dim3, blockIdx/threadIdx/blockDim, cudaStream_t, and the dozen cuda* functions used here.

License#

CECILL-2.1, as the rest of this repo — the same license SHTNS itself is under.

moveopenescclose