Device Routing#
With CUDA JAX installed, JAX places everything on the GPU — including the small,
dispatch-bound programs where a CPU finishes several times faster. hamon's entry
points therefore take a device argument:
"auto"(default) — with no accelerator visible, placement is untouched. Otherwise the work score (n_chains × free nodes) decides: small workloads run on the CPU, large ones on the accelerator."cpu"/"gpu"— that platform, raising if it is not visible.- a concrete
jax.Device— used as-is. None— hamon never touches placement.
The default threshold (4096, the steady-state crossover measured on an RTX 5080)
can be overridden with HAMON_DEVICE_THRESHOLD; calibrate your own with
python benchmarks/device_crossover.py. HAMON_DEVICE=cpu|gpu|none forces a
choice without code changes.
Very short one-shot flows are compile-dominated and can favor the CPU regardless
of size — pass device="cpu" for those, or enable the persistent compile cache
so repeated runs skip GPU compilation entirely.
hamon.resolve_device(device: DeviceLike = 'auto', *, score: int | None = None, threshold: float | None = None) -> JaxDevice | None
#
Resolve a device spec into a concrete jax.Device or None.
None means "leave placement alone" and is what "auto" resolves to
when no accelerator is visible, so CPU-only installs behave identically
with or without routing. With an accelerator present, "auto" routes to
it when score meets the threshold and to the CPU otherwise (or when no
score is supplied). Explicit "cpu"/"gpu"/"tpu" requests fail
loudly if the device is absent.
hamon.work_score(n_chains: int, n_nodes: int) -> int
#
The routing heuristic's work estimate: width of the parallel front.
Rounds are deliberately excluded — the round loop is a single jitted scan, so per-round dispatch does not scale with round count, and compile cost is amortized by the persistent compilation cache.
hamon.enable_persistent_compile_cache(path: str | None = None) -> str | None
#
Turn on JAX's persistent compilation cache (idempotent).
XLA compile dominates the cold cost of NRPT and especially the multi-probe autotuning search (each chain count and each n_expl recompiles the round loop). The persistent cache stores compiled executables on disk and reuses them across processes — measured ≈ −72% wall on repeat cold runs — which is what keeps autotuning affordable.
Only enabled on an accelerator backend. On a CPU-only backend XLA's AOT
loader logs a "machine-feature mismatch" error (warning of a theoretical
SIGILL) for every reloaded executable, and CPU compiles are cheap, so caching
there is net-negative; the test conftest makes the same accelerator-only
choice. An explicit JAX_COMPILATION_CACHE_DIR overrides this on any
backend (the user's deliberate opt-in/out wins).
If JAX_COMPILATION_CACHE_DIR is already set in the environment its
directory is respected (including opting out via an empty value). Otherwise,
when an accelerator is present, the cache dir is set to path (or
~/.cache/jax by default), matching the GPU default used in the test
suite. Whenever caching is active the persistence thresholds are lowered (see
:func:_lower_persistence_thresholds).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | None
|
cache directory; defaults to |
None
|
Returns:
| Type | Description |
|---|---|
str | None
|
The active cache directory, or |
str | None
|
backend, or an empty env var). |
hamon.DeviceLike
module-attribute
#
Represent a PEP 604 union type
E.g. for int | str