# Performance and Reproduction
Performance is measured, not inferred from the presence of assembly. The crate
keeps scalar arithmetic in Rust and exposes whole-buffer conversion so dispatch
and vector work can be amortized. No allocation occurs in library operations.
## Measurements
Measured on 2026-09-06 with an Intel Core i7-11700K, Linux x86-64,
`rustc 1.97.1 (8bab26f4f 2026-07-14)`, LLVM 22.1.6. The process was pinned to
logical CPU 2. Each result is the median of seven timed samples after warmup;
inputs are deterministic pseudorandom values in `[0, 1]`, and both input and
output are black-boxed. Lower is better.
Generic x86-64 build, default features, **4,096 values**:
| Exact encoder, portable Rust loop | 1.749 |
| Exact encoder, bulk AVX2 dispatch | 0.322 |
| Exact encoder, `f64` rounding reference | 1.838 |
| Decoder, integer scalar loop | 0.980 |
| Decoder, bulk API | 0.170 |
| Decoder, ordinary division loop | 0.175 |
| Normalized multiplication, integer operator loop | 0.088 |
| Normalized multiplication, `f64` rounding reference | 2.385 |
The bulk encoder was **5.4x faster** than the equivalent portable loop. Bulk
decoding deliberately retains ordinary vectorizable division on this target:
forcing scalar bit normalization would be a regression. The multiplication
comparison shows the benefit of direct integer UNORM arithmetic, but the `f64`
reference is an accuracy oracle, **not** a claim about the best possible competing
implementation or the old release.
For **1,048,576 values**, generic bulk encoding measured **0.364 ns/value** versus
**1.770 ns/value** for the portable loop. With default features disabled, the
4,096-value bulk encoder measured **1.745 ns/value**, matching the portable path.
With `-C target-cpu=native`, this CPU enables AVX-512. The compiler-vectorized Rust
encoder measured **0.224 ns/value** through the bulk API for 4,096 values, and
**0.274 ns/value** for 1,048,576 values. AVX-512 builds therefore bypass the
handwritten AVX2 kernel rather than limiting the compiler to a narrower ISA.
These are independent-element **throughput**, not single-operation latency.
Scheduling, clocks, compiler versions, cache residency, working-set size, input
distribution, and surrounding code can change the results. Small inputs can be
dominated by call/loop overhead. No performance claim is made for unmeasured
ARM, RISC-V, WebAssembly, firmware, or space hardware.
## Run the Benchmark
The stable, dependency-free benchmark harness is `benches/convert.rs`:
```sh
cargo bench --bench convert
cargo bench --bench convert --no-default-features
RUSTFLAGS="-C target-feature=+avx2" cargo bench --bench convert
RUSTFLAGS="-C target-cpu=native" cargo bench --bench convert
```
On Linux, optionally prefix Cargo with `taskset -c 2` to reduce migration noise,
choosing a CPU available to the process. Run benchmarks separately from tests,
compilation jobs, or other CPU-intensive work. The timed region excludes
allocation and input generation, but includes the bulk API's length checks,
warm cached feature detection where applicable, and output stores.
The harness reports 16, 256, 4,096, and 1,048,576 elements. Small slices exercise
the portable path. Long slices exercise the runtime-dispatched kernel if the
CPU and OS support AVX2; all configurations use exactly the same numerical
contract. The first CPUID probe is excluded by warmup, so these numbers do not
describe first-call latency.
## Implementation Choices
- Scalar encoding uses significand, guard, and sticky bits. Scalar decoding uses
a normalized repeating binary fraction. Neither needs a floating-point unit,
division instruction, or lookup table.
- Scalar arithmetic uses saturating byte operations and widened integer
multiplication/division. Ordinary inlining exposes it to LLVM's vectorizer.
- Generic x86-64 encoding dispatches to eight-lane integer-only AVX2 assembly
after validating CPU and OS state, starting at 32 values. No unaligned or
remainder handling is delegated to callers.
- Compile-time AVX2 omits runtime detection. Compile-time AVX-512 stays in Rust
so the compiler can select wider operations. No global CPU flags are required
for distributing a generic binary with runtime acceleration.
- Other architectures use portable Rust. Kernel, UEFI, and SGX targets exclude
the handwritten module entirely, including its feature probe.
- Hardware bulk decoding uses exact division, not approximate reciprocal
multiplication. Soft-float targets retain integer construction.
Use `cargo rustc --release --lib -- --emit=asm` to inspect non-inlined library
code, or `cargo rustc --release --bench convert -- --emit=asm` to inspect the
inlined loops in the benchmark. Enable the same target flags as the application.
Handwritten assembly is isolated in `src/simd.rs`; there is no unchecked public
entry point and no unsound float-to-integer cast shortcut.
## Correctness Before Timing
```sh
cargo test --release --all-features -- --include-ignored
cargo test --release --no-default-features -- --include-ignored
RUSTFLAGS="-C target-feature=+avx2" cargo test --release --all-features -- --include-ignored
RUSTFLAGS="-C target-cpu=native" cargo test --release --all-features -- --include-ignored
```
Only run explicitly targeted binaries on supporting CPUs. Miri verifies slice
casts and portable behavior, not inline assembly. The assembly's numerical
results are checked against an independent reference across the full nontrivial
positive binary32 input range, with separate tests for classification, every
legal alignment modulo 32, and tails. CI compiles explicit AVX2 without assuming
that every hosted runner can execute it.