also see: #1935
Summary
autobahn-python's native story is deliberately sophisticated — NVX (own SIMD C via CFFI) with
fixed -march floors per architecture, a pure-Python auto-fallback, an opt-in native-arch
build (real Gentoo/Arch users), cross-compilation support (real Yocto/Buildroot users),
and a vendored FlatBuffers. Today this is spread across README.md, docs/wheels-inventory.rst,
docs/environments/conda.rst, docs/release-notes.rst, docs/changelog.rst, and docstrings in
src/autobahn/nvx/_compile_args.py. This issue proposes one reference page (RST, under
docs/) consolidating and correcting the material below.
Scope: documentation only — no src/ behaviour change. (One doc correction is needed — see §10.)
Proposed page outline
1. Two-layer model
- Pure-Python core — every hot path (UTF-8 validation, XOR masking) has a pure-Python
implementation → runs on any CPU, any architecture, CPython and PyPy. No ISA floor.
autobahn always runs.
- NVX — optional CFFI accelerator (
_nvx_utf8validator, _nvx_xormasker) for those two paths.
- and both are tested for WebSocket conformance using autobahn-testsuite and pass every test green (in strict mode)
2. ISA floors of the published native wheels
| Target |
-march floor |
Meaning |
| x86-64 |
x86-64-v2 |
SSE3/SSSE3/SSE4.1/SSE4.2, POPCNT, CMPXCHG16B (~2009+) |
| aarch64 |
armv8-a |
the 64-bit ARM baseline — every aarch64 CPU qualifies (no sub-floor case) |
| source build |
same floors; AUTOBAHN_ARCH_TARGET=native opt-in |
host-CPU-locked, maximum performance |
Authoritative source: src/autobahn/nvx/_compile_args.py / get_compile_args():
$ python -c "from autobahn.nvx._compile_args import get_compile_args; print(get_compile_args())"
['-std=c99', '-Wall', '-Wno-strict-prototypes', '-O3', '-march=x86-64-v2']
3. Native & cross builds (Gentoo/Arch, Yocto/Buildroot)
- Native distro builds (Gentoo, Arch):
AUTOBAHN_ARCH_TARGET=native → -march=native, host-CPU
optimized, host-locked. Correct only when the build host is also the run host (single-machine
or per-host package builds).
- Cross-compilation (Yocto, Buildroot, OpenWrt): building e.g. aarch64 on an x86-64 host. Two
rules the page must state (and which _compile_args.py already encodes):
-march=native is host-only and wrong under cross-compilation — it would encode the
build host's ISA into a target binary. NVX therefore keeps native strictly opt-in and
never defaults to it.
platform.machine() reports the build host arch, not the target arch, during a
cross-build — so it must not be used to pick target flags; the vendored/distro toolchain's
own CFLAGS/-march (Buildroot/Yocto-supplied) are respected instead.
- Net: a Buildroot/Yocto cross-build gets the toolchain's baseline (e.g. the SDK's
-march),
not x86-64-v2/native; document how an integrator overrides flags if they want.
4. Runtime selection & fallback — build-availability-based, not CPU-capability-based
From autobahn/websocket/__init__.py:
HAS_NVX = were the CFFI modules importable (try: import _nvx_utf8validator … except ImportError).
USES_NVX = HAS_NVX, unless AUTOBAHN_USE_NVX=0 (force pure Python) or =1 while not built (loud RuntimeError).
Handled gracefully: not built → pure Python; explicitly disabled → pure Python.
⚠️ The one gap to document prominently: a v2 wheel on a sub-v2 CPU. import _nvx_utf8validator succeeds (dlopen loads the object; the SSE4.x code only executes when
validate()/mask() is called), so except ImportError never fires → USES_NVX=True →
SIGILL on the first native call. The probe is a build-capability check, not a CPU-
capability check. Escape: AUTOBAHN_USE_NVX=0 → pure Python. (Cross-link the proposed
runtime ISA-floor-guard issue, which would close this by gating NVX on the CPU meeting the floor.)
5. Build-side hardening (already shipped, #1856 — document it)
AUTOBAHN_USE_NVX=1 requesting NVX while the CFFI extension fails to compile now aborts the
build hard (non-zero exit) instead of silently degrading to a pure-Python py3-none-any wheel.
A transient native-compile crash (e.g. a gcc SIGSEGV under QEMU ARM64 emulation) therefore fails
CI so it can retry, rather than uploading a structurally valid but unintended artifact.
AUTOBAHN_USE_NVX=0 still produces a pure-Python wheel as before.
6. How to determine a wheel's ISA
file / ldd cannot tell you the micro-arch level (only x86-64 + libs).
- Contract:
get_compile_args() (the -march) — the minimum promised ISA.
readelf -n <so> | grep -i x86 — GNU ISA property if emitted (often absent for -march=v2 without -fcf-protection).
objdump -d <so> | grep -cE '%ymm|%zmm' (AVX/AVX-512 present?) + grep v2/SSE4.2 tells (pcmpistri popcnt crc32 ptest pblendvb round[ps][sd] pextrb pinsrb).
elfx86exts — the purpose-built tool. Install via apt (elfx86exts), not cargo: a stable
measurement tool for a reproducible gate wants a pinned, prebuilt, toolchain-free binary; cargo install floats and needs Rust, buying nothing for ISA-level detection. Worked NVX example:
$ elfx86exts _nvx_utf8validator.cpython-311-x86_64-linux-gnu.so
SSE2 (pxor) SSE41 (pextrb) ... CPU Generation: Penryn
→ actual highest instruction = SSE4.1 (Penryn, 2007), comfortably inside the declared
-march=x86-64-v2 contract. Distinguish contract (v2, what you support) from emitted floor
(SSE4.1, what's in this binary).
7. elfx86exts limits (important)
elfx86exts reports the union of instructions present (static). For NVX this equals the
requirement, because NVX is a fixed -march, no-runtime-dispatch build. For runtime-CPUID-
dispatching binaries the union is not the requirement (they bundle SSE→AVX-512 paths and pick
one at runtime) — never apply "elfx86exts max = required ISA" to a dispatching third-party binary.
NVX is the easy case precisely because it doesn't dispatch.
8. How to determine a machine's capability
grep -m1 '^flags' /proc/cpuinfo — check for the feature (sse4_2, avx2, …). Include the
elfx86exts→/proc/cpuinfo flag map (SSE41→sse4_1, AVX2→avx2, AVX512→avx512f, AES→aes,
PCLMUL→pclmulqdq, BMI2→bmi2, ADX→adx).
/lib64/ld-linux-x86-64.so.2 --help | grep -i 'x86-64-v' → the CPU's x86-64-vN level.
- Ground truth: run a real workload — a missing required instruction faults immediately with SIGILL.
9. Vendored FlatBuffers (autobahn's other native/vendoring dimension)
autobahn also vendors FlatBuffers (schema + flatc codegen) and exposes it from Python,
alongside NVX. The page should note the vendoring + version-sync policy — importantly, zlmdb also
vendors FlatBuffers, and both copies must stay version-synced (see the flatbuffers sync-check work
and the FlatBuffers-pair coupling). This matters downstream because crossbar depends on both
autobahn and zlmdb (see §12).
10. Consolidate scattered docs — and correct one inaccuracy
Fold in / cross-link: README.md NVX section, docs/wheels-inventory.rst (AUTOBAHN_USE_NVX=0/1),
docs/environments/conda.rst, docs/release-notes.rst, docs/changelog.rst (#1856).
★ Correction: docs/release-notes.rst currently says NVX is "Automatically enabled on x86_64
CPUs with AVX2 support." This is inaccurate on two counts: (a) the floor is x86-64-v2
(SSE4.2, not AVX2/v3); (b) enablement is import-based (HAS_NVX), not AVX2-CPU-gated. The new
page should state the correct floor + fallback semantics and the release note should be fixed.
11. Compatibility table — the asgard fleet (worked example)
| Node |
CPU |
ISA level |
pure Python |
published NVX wheel |
| asgard1 |
Xeon D-1587 (Broadwell) |
x86-64-v3 (AVX2) |
✓ |
accelerated ✓ |
| asgard4 |
Celeron J3455 (Goldmont) |
x86-64-v2 (SSE4.2, no AVX) |
✓ |
accelerated ✓ (floor exactly met) |
| asgard2/3 |
Atom D525 (Pineview) |
~baseline (no SSE4.x) |
✓ |
SIGILL on first NVX call → set AUTOBAHN_USE_NVX=0 → pure Python |
Punchline: autobahn has no hard ISA cliff — there is always a working pure-Python path —
unlike a closed native binary with no fallback. The only sharp edge is the sub-v2-wheel case (§4).
12. Stack context (why this matters beyond autobahn)
crossbar depends on both autobahn and zlmdb. The native/vendoring/cross-compile story spans
all three:
- autobahn vendors FlatBuffers + NVX (own SIMD C,
-march floors),
- zlmdb vendors LMDB (patched) + FlatBuffers (portable C, no ISA floor),
- crossbar consumes both → the union.
The FlatBuffers copies in autobahn and zlmdb must stay version-synced. Cross-link the sibling
zlmdb docs issue.
13. FAQ
- "SIGILL on an old CPU" →
AUTOBAHN_USE_NVX=0 (or install without NVX); see the runtime-guard issue.
- PyPy: NVX is CFFI (works on CPython and PyPy); pure-Python paths JIT well on PyPy.
Acceptance criteria
Notes
- Documentation only — no
src/ change, no dependency change; autobahn stays MIT.
- Related: proposed runtime NVX ISA-floor guard (graceful fallback instead of SIGILL) and the
check-wheel-isa build gate (assert built wheels don't exceed the declared -march floor via
elfx86exts, apt, on the outer runner + objdump fallback in-container); sibling zlmdb native/
vendoring docs issue.
- The RST page itself is authored and human-reviewed per
AI_POLICY.md; this issue captures the substance/outline.
also see: #1935
Summary
autobahn-python's native story is deliberately sophisticated — NVX (own SIMD C via CFFI) with
fixed
-marchfloors per architecture, a pure-Python auto-fallback, an opt-in native-archbuild (real Gentoo/Arch users), cross-compilation support (real Yocto/Buildroot users),
and a vendored FlatBuffers. Today this is spread across
README.md,docs/wheels-inventory.rst,docs/environments/conda.rst,docs/release-notes.rst,docs/changelog.rst, and docstrings insrc/autobahn/nvx/_compile_args.py. This issue proposes one reference page (RST, underdocs/) consolidating and correcting the material below.Scope: documentation only — no
src/behaviour change. (One doc correction is needed — see §10.)Proposed page outline
1. Two-layer model
implementation → runs on any CPU, any architecture, CPython and PyPy. No ISA floor.
autobahn always runs.
_nvx_utf8validator,_nvx_xormasker) for those two paths.2. ISA floors of the published native wheels
-marchfloorx86-64-v2armv8-aAUTOBAHN_ARCH_TARGET=nativeopt-inAuthoritative source:
src/autobahn/nvx/_compile_args.py/get_compile_args():3. Native & cross builds (Gentoo/Arch, Yocto/Buildroot)
AUTOBAHN_ARCH_TARGET=native→-march=native, host-CPUoptimized, host-locked. Correct only when the build host is also the run host (single-machine
or per-host package builds).
rules the page must state (and which
_compile_args.pyalready encodes):-march=nativeis host-only and wrong under cross-compilation — it would encode thebuild host's ISA into a target binary. NVX therefore keeps
nativestrictly opt-in andnever defaults to it.
platform.machine()reports the build host arch, not the target arch, during across-build — so it must not be used to pick target flags; the vendored/distro toolchain's
own
CFLAGS/-march(Buildroot/Yocto-supplied) are respected instead.-march),not
x86-64-v2/native; document how an integrator overrides flags if they want.4. Runtime selection & fallback — build-availability-based, not CPU-capability-based
From
autobahn/websocket/__init__.py:HAS_NVX= were the CFFI modules importable (try: import _nvx_utf8validator … except ImportError).USES_NVX = HAS_NVX, unlessAUTOBAHN_USE_NVX=0(force pure Python) or=1while not built (loudRuntimeError).Handled gracefully: not built → pure Python; explicitly disabled → pure Python.
import _nvx_utf8validatorsucceeds (dlopen loads the object; the SSE4.x code only executes whenvalidate()/mask()is called), soexcept ImportErrornever fires →USES_NVX=True→SIGILL on the first native call. The probe is a build-capability check, not a CPU-
capability check. Escape:
AUTOBAHN_USE_NVX=0→ pure Python. (Cross-link the proposedruntime ISA-floor-guard issue, which would close this by gating NVX on the CPU meeting the floor.)
5. Build-side hardening (already shipped, #1856 — document it)
AUTOBAHN_USE_NVX=1requesting NVX while the CFFI extension fails to compile now aborts thebuild hard (non-zero exit) instead of silently degrading to a pure-Python
py3-none-anywheel.A transient native-compile crash (e.g. a
gccSIGSEGV under QEMU ARM64 emulation) therefore failsCI so it can retry, rather than uploading a structurally valid but unintended artifact.
AUTOBAHN_USE_NVX=0still produces a pure-Python wheel as before.6. How to determine a wheel's ISA
file/lddcannot tell you the micro-arch level (onlyx86-64+ libs).get_compile_args()(the-march) — the minimum promised ISA.readelf -n <so> | grep -i x86— GNU ISA property if emitted (often absent for-march=v2without-fcf-protection).objdump -d <so> | grep -cE '%ymm|%zmm'(AVX/AVX-512 present?) + grep v2/SSE4.2 tells (pcmpistri popcnt crc32 ptest pblendvb round[ps][sd] pextrb pinsrb).elfx86exts— the purpose-built tool. Install via apt (elfx86exts), not cargo: a stablemeasurement tool for a reproducible gate wants a pinned, prebuilt, toolchain-free binary;
cargo installfloats and needs Rust, buying nothing for ISA-level detection. Worked NVX example:-march=x86-64-v2contract. Distinguish contract (v2, what you support) from emitted floor(SSE4.1, what's in this binary).
7. elfx86exts limits (important)
elfx86extsreports the union of instructions present (static). For NVX this equals therequirement, because NVX is a fixed
-march, no-runtime-dispatch build. For runtime-CPUID-dispatching binaries the union is not the requirement (they bundle SSE→AVX-512 paths and pick
one at runtime) — never apply "elfx86exts max = required ISA" to a dispatching third-party binary.
NVX is the easy case precisely because it doesn't dispatch.
8. How to determine a machine's capability
grep -m1 '^flags' /proc/cpuinfo— check for the feature (sse4_2,avx2, …). Include theelfx86exts→
/proc/cpuinfoflag map (SSE41→sse4_1, AVX2→avx2, AVX512→avx512f, AES→aes,PCLMUL→
pclmulqdq, BMI2→bmi2, ADX→adx)./lib64/ld-linux-x86-64.so.2 --help | grep -i 'x86-64-v'→ the CPU's x86-64-vN level.9. Vendored FlatBuffers (autobahn's other native/vendoring dimension)
autobahn also vendors FlatBuffers (schema +
flatccodegen) and exposes it from Python,alongside NVX. The page should note the vendoring + version-sync policy — importantly, zlmdb also
vendors FlatBuffers, and both copies must stay version-synced (see the flatbuffers sync-check work
and the FlatBuffers-pair coupling). This matters downstream because crossbar depends on both
autobahn and zlmdb (see §12).
10. Consolidate scattered docs — and correct one inaccuracy
Fold in / cross-link:
README.mdNVX section,docs/wheels-inventory.rst(AUTOBAHN_USE_NVX=0/1),docs/environments/conda.rst,docs/release-notes.rst,docs/changelog.rst(#1856).★ Correction:
docs/release-notes.rstcurrently says NVX is "Automatically enabled on x86_64CPUs with AVX2 support." This is inaccurate on two counts: (a) the floor is
x86-64-v2(SSE4.2, not AVX2/v3); (b) enablement is import-based (
HAS_NVX), not AVX2-CPU-gated. The newpage should state the correct floor + fallback semantics and the release note should be fixed.
11. Compatibility table — the
asgardfleet (worked example)AUTOBAHN_USE_NVX=0→ pure PythonPunchline: autobahn has no hard ISA cliff — there is always a working pure-Python path —
unlike a closed native binary with no fallback. The only sharp edge is the sub-v2-wheel case (§4).
12. Stack context (why this matters beyond autobahn)
crossbar depends on both autobahn and zlmdb. The native/vendoring/cross-compile story spans
all three:
-marchfloors),The FlatBuffers copies in autobahn and zlmdb must stay version-synced. Cross-link the sibling
zlmdb docs issue.
13. FAQ
AUTOBAHN_USE_NVX=0(or install without NVX); see the runtime-guard issue.Acceptance criteria
docs/(e.g.docs/nvx_cpu_isa.rst), linked from the docs index and installation page.-marchfloors (v2 / armv8-a / native opt-in); native & cross builds (Gentoo/Arch, Yocto/Buildroot,platform.machine()+nativecaveats); build-availability-vs-CPU-capability fallback + the sub-v2 SIGILL caveat; the [BUG] Sync.cicdsecurity fix and hardenhatch_build.pyagainst silent CFFI build degradation (release-readiness for 26.6.1) #1856 build-hardening; determining wheel ISA (elfx86exts+ dispatch limits); determining CPU capability; vendored FlatBuffers + sync note; theasgardtable.docs/release-notes.rst"AVX2" claim corrected.Notes
src/change, no dependency change; autobahn stays MIT.check-wheel-isabuild gate (assert built wheels don't exceed the declared-marchfloor viaelfx86exts, apt, on the outer runner +objdumpfallback in-container); sibling zlmdb native/vendoring docs issue.
AI_POLICY.md; this issue captures the substance/outline.