Skip to content

serialize.cs 1.5.0 — precomputed compressed float entry points

Choose a tag to compare

@gafferongames gafferongames released this 21 Aug 03:47
96a9240

The precomputed compressed float entry points — the C# port's leg of mas-bandwidth/schema#82, mirroring the C++ reference's 1.11.0 — reach a released version. Additive throughout: SerializeCompressedFloat is untouched on every surface.

New public API

SerializeUtil.CompressedFloatParams(min, max, resolution,
    out uint maxIntegerValue, out int bits, out float delta);

stream.SerializeCompressedFloatPrecomputed(ref value, maxIntegerValue, bits, delta, min);

The derivation — a float32 divide, a clamp, a ceiling and BitsRequired — depends only on the declaration, never on the value, so a schema compiler can run it at code generation time and hand the results to every call site. The entry point joins IBitStream, so it exists on WriteStream, ReadStream, MeasureStream and both batch surfaces.

Wire bytes are identical to SerializeCompressedFloat by construction: the derive-per-call entry point runs exactly CompressedFloatParams and then exactly the precomputed path's arithmetic. test_compressed_float_precomputed_differential holds four implementations — including a frozen verbatim copy of the pre-split v1.4.0 arithmetic — to identical measured bits, wire bytes, read acceptance and decoded bit patterns (never tolerance), over 18 declarations and 4,441,842 checks.

What is new since the API merged

The cross-language gate now covers it. The interop harness's FMA-boundary field (0.005f in [0,10] res 0.01) writes through SerializeCompressedFloatPrecomputed, with the constants its declaration derives. No byte moves, and the C++ half still writes it plainly — but the new entry point is now held under byte identity against the real serialize.h, in both directions, at the value that discriminates a strictly evaluated quantization from a contracted one.

The JIT verdict is measured, and it says do not adopt for speed. At a call site with literal min/max/resolution — what a schema compiler emits — RyuJIT already constant-folds the whole derivation: both entry points compile to the same 2360 bytes of arm64 machine code, with no ceiling and no BitsRequired in either, and measure the same (1.99 ns per field written, 1.90 read). Reach for the precomputed path for the single audited home. Where the declaration is not a compile-time constant the derivation does run per field, and the entry point removes it: 2.82 → 2.19 ns written, 2.71 → 2.28 read.

Compatibility

No wire change, no behavior change, no API removed or deprecated. Interop pin unchanged at C++ serialize v1.7.0. netstandard2.1 (Unity-class), net8.0 and net10.0 as before.