Skip to content

v4.0.0-rc.8

Pre-release
Pre-release

Choose a tag to compare

@github-actions github-actions released this 04 Nov 15:02
· 180 commits to main since this release

Install: dotnet add package Nino --version 4.0.0-rc.8 (Unity package: 4.0.0-preview.208)

Other languages: go get github.com/JasonXuDeveloper/Nino/runtimes/go/v4@v4.0.0-rc.8, cargo add nino-serialization@=4.0.0-rc.8, npm install nino-serialization@4.0.0-rc.8.

Wire format

Only these write different bytes than v4.0.0-rc.7:

  • On 32-bit x86, a member whose type is a struct (not a [NinoType]) holding a TimeOnly, Complex, Int128 or UInt128 after a smaller field is now written in the canonical layout, as on 64-bit runtimes, instead of its x86 memory.
  • Types with a member that is a tuple of eight items or more now write it, every item in order as for shorter tuples, where they wrote nothing.
  • T?[] and other T? sequences of a raw [NinoType] struct whose formatter the runtime resolver builds (types no generated code names, serialized by Type or boxed) are written at the fixed stride generated code uses, so either side reads the other's bytes; payloads such a runtime-bound formatter wrote before must be rewritten.
  • Unmanaged structs that are not [NinoType] and hold an Int128 or UInt128 now write their padding as zeros on .NET 8 and later, as the canonical layout requires (it was copied from memory, so it held whatever was there).
  • A closed generic [NinoType] struct (Pad<int>) held as a member or collection element is written member by member, as its formatter writes it everywhere else, unless its definition is raw; it was copied as raw memory with its padding there.
  • Every string member and element (and StringBuilder, TimeZoneInfo's id, [NinoUtf8] strings) is written with a 1, 3 or 5 byte header instead of a 4-byte one: null is 00, "" is 01, "abc" is 07 61 62 63, and a string of 64 to 32,767 chars starts with 80 and a 2-byte value. Strings up to 63 chars save 3 bytes each: 1,000 mixed short strings take 20,848 bytes instead of 23,848 ([NinoUtf8]: 21,884 instead of 27,884). Data written by earlier 4.0 prereleases is not readable.
  • The TypeScript runtime (the nino.ts that nino-gen writes) writes and reads strings with the 1, 3 or 5 byte headers .NET writes from rc.8 on; data written by earlier versions is not readable.
  • The Go runtime writes and reads strings with the 1, 3 or 5 byte headers .NET writes from rc.8 on; data written by earlier versions of the module is not readable.
  • The Rust runtime (nino-serialization) writes and reads strings with the 1, 3 or 5 byte headers .NET writes from rc.8 on; data written by earlier versions of the crate is not readable.
  • On 32-bit x86 .NET (Windows), raw structs (plain structs, and [NinoType] structs whose layout differs between runtimes) holding a DateTime at an offset the canonical layout pads are now written in the canonical layout, as on 64-bit runtimes, instead of their x86 memory.
  • On .NET Framework (64-bit and 32-bit), raw structs holding a DateTime whose fields the runtime reorders are now written in the canonical layout, as on 64-bit .NET, instead of their memory.

New

  • A Go runtime (runtimes/go, package nino) that reads and writes Nino's binary format, for code generated or written in the style of the TypeScript runtime.
  • Struct tags for the Go runtime: nino.Marshal and nino.Unmarshal read and write tagged Go structs without generated code, checked against the .NET layout a type declares with NinoLayout().
  • A Rust runtime (runtimes/rust, not yet published) reads and writes Nino's wire format, deep nesting and version-tolerant and keyed types included, with no unsafe code and no panics on any input.
  • Rust runtime: a serde feature implementing serde for Nino's value types, and FromStr for Decimal, Guid and BigInteger, so structs with Nino codecs can derive serde as well.
  • Rust runtime: #[derive(NinoRead, NinoWrite)] (feature derive) with #[nino(...)] attributes for the format, kind, type id, member order, keys and [NinoUtf8], checked against a published layout at compile time.
  • Warning NINO027 flags [NinoFormerName] on a generic type that is written with a type id: every instantiation would share the one id and collide.
  • NinoTypeLayoutAttribute.Deferrable says whether a type's bodies nested past 128 class bodies are deferred, and the layout's text form carries it as deferrable=true|false, so code generators for other languages read it instead of re-deriving .NET's rules.
  • Go and Rust read whether a type is deferrable from its published layout (deferrable=true|false); the Rust derive's deferrable attribute is now optional when layout is given.
  • Go code generation: nino-gen --lang go writes Go structs (with nino tags and json/yaml tags, camelCase by default) and codecs for an assembly's [NinoType]s, for the Go runtime in runtimes/go.
  • Rust code generation: nino-gen --lang rust writes Rust structs and codecs for an assembly's [NinoType]s, for the nino-serialization crate, with optional serde derives (--serde).
  • nino-gen (.NET tool Nino.Codegen): writes TypeScript, Go or Rust types and codecs for the [NinoType]s of a built assembly, from the layouts the generator publishes in it (nino-gen --assembly <dll> --lang ts|go|rust --out <dir>).
  • NinoDataException (namespace Nino.Core, derived from InvalidOperationException) is thrown for every invalid input Nino rejects while deserializing, so untrusted data can be caught with one specific type.
  • NinoSettings.MaxDepth (default 512, at least 128) and NinoSettings.MaxCollectionLength (default int.MaxValue) bound nesting and the element count a collection in the data may claim; set them before the first serialization or deserialization (later they throw).
  • NinoSettings.MaxHashCollisions (default 100): a key read from the data that finds more keys than this in its hash bucket throws NinoDataException, so data built to make keys collide (hash flooding) can no longer make reading a Dictionary, HashSet, lookup, ImmutableDictionary or ImmutableHashSet quadratic.
  • NinoSettings.Untrusted (off by default): for data from someone who could craft it, the dictionaries and sets Nino creates hash integer, char, Guid and string keys with a per-process secret, raw structs are checked as their constructors would (bool, DateTime, decimal, DateOnly, TimeOnly), and bytes left after the value throw NinoDataException. Off, it costs nothing.
  • Generic [NinoType]s with non-public members no longer need to be partial when the project targets .NET 9 or later: generated code reaches the members through [UnsafeAccessor], as it does for non-generic types since .NET 8.
  • Nino.Codegen writes your [NinoType]s as TypeScript, Go or Rust after each build: declare <NinoCodegen Include="typescript" Out="ts/" /> (or go, rust) with nino-gen's options as metadata. The nino-gen command is now the Nino.Codegen.Tool dotnet tool. Nino.Codegen replaces Nino.TypeScript, whose NinoTypeScript* properties still work with a warning (NINOGEN003).
  • nino-gen generates generic [NinoType]s as generic types in TypeScript, Go and Rust (Box<T>, Box[T any], BoxType<T>), with codecs built from those of their type arguments (BoxCodec(...)), and closed uses such as Box<int> as members.
  • nino-gen's --maps indexmap|std (MSBuild metadata Maps) for Rust: std writes std::collections::HashMap and HashSet instead of indexmap's, with no dependency; they are written in HashMap's iteration order. indexmap stays the default.
  • nino-gen generates closed generic types in polymorphic and object slots (a generic subtype such as Box<int> : Animal) for TypeScript, Go and Rust: the closed types the assembly's code names are read from its metadata, each a case of the slots it is assignable to, behind the id of its closed .NET name.
  • The Go, Rust and TypeScript runtimes are published with every release, with Nino's version: go get github.com/JasonXuDeveloper/Nino/runtimes/go/v4 (the module path now ends in /v4), cargo add nino-serialization (with the derive feature for #[derive(NinoRead, NinoWrite)]) and npm install nino-serialization (generate with Runtime="nino-serialization" to import it).
  • The Unity package includes optional Burst-compiled kernels (Nino.Burst): with com.unity.burst installed, arrays of tuples and structs, raw structs with padding and Latin-1 strings are moved with SSSE3 or AArch64 Neon instead of Nino's scalar netstandard paths, with the same bytes.
  • useWasm() in the TypeScript runtime: longer strings are read and written through a small embedded WebAssembly module in browsers and Deno, with the JavaScript paths kept where WebAssembly SIMD, GC or the JS String Builtins are unavailable or blocked; useWasm(false) turns it off.
  • Reference tracking (trackReferences) in the TypeScript, Go and Rust runtimes, for the code nino-gen writes; Rust holds tracked objects as nino::Shared<T>.
  • nino-gen generates generic types written behind a type id (non-sealed, abstract, interface), the closed types their slots need, Nullable and KeyValuePair type arguments inside collections, reference tracking (trackReferences) and [NinoType] collections for TypeScript, Go and Rust, and generic types holding themselves with other type arguments for TypeScript.
  • deserializeExact in the TypeScript runtime (nino-serialization): deserialize that also throws a NinoError when bytes follow the value, as Go's DeserializeExact and Rust's deserialize_exact do.
  • Nino.WebGL in the Unity package: WebGL players built with WebAssembly SIMD (Unity 6's Target WebAssembly 2023, or --compiler-flags=-msimd128 on 2022.3) move Latin-1 strings of 16 chars or more and arrays of tuples and padded raw structs of 32 or more with wasm SIMD kernels, the same bytes as without them.
  • NinoKernels is now a sealed table of native function pointers (delegate* unmanaged[Cdecl] properties Permute, Narrow, Widen and the new NarrowAscii and WidenAscii for [NinoUtf8] strings, with MinBlocks and MinChars), which NinoKernels.Register copies into static fields so each kernel is one direct call; it replaces the abstract class of earlier builds, so a subclass of it must become a table of cdecl functions.
  • nino-gen's --numeric-arrays typed (NumericArrays="typed") generates arrays and lists of fixed-size numbers in TypeScript as typed arrays (Int32Array, Float64Array, BigInt64Array...), copied in one block: faster to serialize in Node and Chromium and to deserialize in Node, 2-3x slower to deserialize in Chromium, so the default stays number[].
  • The MSBuild property <NinoPrefetchLargeCollections>true</NinoPrefetchLargeCollections>: top-level Serialize of a T[] or List<T> of a non-generic, non-fixed-size [NinoType] class, and Deserialize(data, ref collection) into one, prefetch the objects ahead once the collection outgrows a quarter of the last-level cache and its objects are not in allocation order (.NET 8+ on x64 and Arm64, through the interceptors; not Unity). The bytes are the same; unset, the generated code is unchanged.
  • Go: Reader.EnterBody/ExitBody and Writer.EnterBody/ExitBody open a class body in place for code that calls the body directly (falling back to ReadClassInto/WriteClass from 128 open bodies on), and nino.UniqueEntries checks pairs read by your own loop for a repeated key; nino-gen's Go code uses both.
  • Go: nino.UniqueValues checks the elements of a set read by your own loop for a repeated value (the counterpart of UniqueEntries); nino-gen's Go code reads sets with direct calls and checks them with it.
  • Rust nino::deserialize_into(&bytes, &mut value) and NinoRead::read_into read into an existing value as .NET's Deserialize(data, ref value) does: nested objects read in place, Vec buffers and elements reused, maps and sets cleared and refilled, polymorphic values reused only for the same type.
  • TypeScript deserializeInto(codec, data, value) reads into an existing value as .NET's Deserialize(data, ref value) does: nested objects read in place, arrays keep their storage and elements, maps and sets cleared and refilled, polymorphic members reused only for the same class.
  • Go nino.DeserializeInto(data, &v, ReadXInto) reads into an existing value as .NET's Deserialize(data, ref value) does: nested objects read in place, slices keep their storage and elements, polymorphic values reused only for the same type; nino-gen writes ReadXInto for every type.
  • Code fixes for NINO024 (remove a member's [NinoFormerName] in a type that is not keyed) and NINO025 (remove a [NinoFactory] that [NinoConstructor] makes unused); NINO003, NINO005, NINO007, NINO010, NINO011, NINO012 and NINO017 now say what happens and what to change.
  • Rust: nino::sealed_read!() and nino::sealed_write!() write a sealed class's NinoRead and NinoWrite methods in a hand-written impl, as generated code does.
  • Go (breaking): keyed members are read and written with BeginMember, EndMember, SkipMember and KeyedMember, which replace BeginEntry, EndEntry, SkipEntry and KeyedEntry; the duplicate checks are CheckUniqueKeys, CheckUniqueValues and CheckUniqueStrings. Regenerate Go code with nino-gen and rename the calls in hand-written codecs.
  • Rust: NinoWrite::PREFETCH_BYTES replaces NinoWrite::PREFETCH, which still works, deprecated; the derive's format takes "positional" and "version_tolerant" ("plain" and "tolerant" are still accepted).
  • nino-gen: --numeric-arrays (NumericArrays) takes plain (the default, number[]) or typed, instead of array or typed.
  • Go (breaking): ReadSlice, WriteSlice, ReadMap, WriteMap, ReadEntries, WriteEntries, ReadSet, WriteSet, ReadUnique, ReadUniqueEntries, ReadFixed, WriteFixed, WritePrimitivesRaw, ReadPrimitivesInto and ReadSealedInto are removed: generated code no longer calls them. Regenerate Go code written by an older nino-gen, and write hand-written collection loops with direct calls.
  • Rust (breaking): the depth constants are INLINE_DEPTH and MAX_DEPTH, which replace DEPTH_INLINE and DEPTH_MAX.
  • TypeScript (breaking): the NinoReader and NinoWriter methods generated code calls are beginRegion and exitBody, which replace region and exit; regenerate code an earlier nino-gen or Nino.TypeScript wrote.
  • TypeScript (breaking): the NinoReader method generated code calls to read a presence byte is present (was notNull), with presentAt for a presence byte at a given offset, and the subtype registry is NinoSubtypes (was NinoRegistry); regenerate code an earlier nino-gen or Nino.TypeScript wrote.
  • Renamed before 4.0: Nino.Core.NinoPolymorphic<TBase> (4.0.0-rc.7), the subtype registry generated code calls, is now Nino.Core.CompilerServices.NinoSubtypes<TBase>; the user API NinoPolymorphic.Register keeps its name.
  • NINO028 warns about [NinoMember] on a member Nino never serializes (static, constant, indexer, a property without a setter that no constructor takes, a backing field), where NINO010 wrongly said it was collected already; a code fix removes it. NINO008 now names the member and its type.

Performance

  • TypeScript runtime in Chromium: a text-heavy article reads 27-31% faster on GitHub's x64 and Arm64 runners, and a list of mixed ASCII, Latin-1 and other strings writes about 12% faster on x64; Node is unchanged.
  • Go runtime strings: writing mixed text is 1.9x faster on Arm64 (NEON, on par with MessagePack there) and 2.7x faster on x64 without AVX2 (SSSE3); reading it is 17% faster on Arm64 and 34% faster on x64 without AVX-512 VBMI (GitHub runners: Neoverse-N2, AMD EPYC 7763 with the tier forced).
  • Unity x64 players on AVX2 CPUs: arrays of small tuples and structs (up to 16 bytes, 256 values or more) read up to 3.5 times and written up to 1.3 times as fast as with the SSSE3 kernels (IL2CPP, Burst AVX2 target).
  • In Node, writing strings of up to 48 chars no longer goes through a regex and a native call: serializing 1000 ASCII strings takes 0.025-0.028 ms instead of 0.060-0.065 (x64 and Arm64), 1000 mixed strings 0.056-0.059 instead of 0.090, the Lookup case 0.39 ms instead of 0.74-0.78, OrderBook 0.43 instead of 0.76-0.79 and Player 0.26 instead of 0.31-0.34. Browsers are unchanged.
  • Rust: writing strings with non-ASCII chars is up to 2.4x faster on x64 with AVX2 or AVX-512 (strings-mixed-1000 serialize 37% faster on an AVX-512 runner, 18% on an AVX2 one) and up to 5x on Arm64 for text over 64 bytes; reading Latin-1 and UTF-16 strings of 32 bytes or more is up to 2.4x faster on x64 and 1.6x on Arm64.
  • Rust: [NinoUtf8] strings are written up to 45% faster and read 10% to 14% faster (strings-mixed-utf8-1000, x64 and Arm64 runners).
  • TypeScript in Node: 1,000 CJK and Cyrillic strings of 4 to 16 chars serialize in 0.018 ms instead of 0.066 on x64 and 0.034 instead of 0.108 on Arm64 (69-73% less time) and deserialize 46-47% faster; CJK strings of 24 to 40 chars read and write 5-7% faster (GitHub's x64 and Arm64 runners).
  • Go: strings are written and read faster on x64 and Arm64: a list of 1,000 short ASCII strings serializes in 33-40% less time and deserializes in 17-27% less, mixed strings serialize 5-14% faster (GitHub runners: AMD EPYC 7763, 9V74 and 9V45, Intel Xeon Platinum, Neoverse-N2).
  • Go: nino.Marshal and nino.Unmarshal of tagged structs with strings, string slices and maps take 52-76% less time to write and 18-45% less to read (the same runners).
  • Go: writing and reading [NinoUtf8] strings that are not ASCII is faster on x64 and Arm64 (a third faster to write and a fifth faster to read a list of mixed strings on x64).
  • Rust: strings are written and read faster on x64 and Arm64: a list of 1,000 short ASCII strings serializes in 11-25% less time and deserializes in 3-6% less, mixed strings in 5-30% and 10-12% less (GitHub runners: AMD EPYC 7763, 9V74 and 9V45, Intel Xeon Platinum, Neoverse-N2).
  • Rust: reading a struct or class with string members takes less time (9 to 15% for the benchmark's record).
  • Writing a list of numbers or other fixed-size values from Rust takes 23 to 52% less time (10,000 i32 or f64), and a record holding a short list of numbers up to 9% less.
  • Writing a record holding a short list of numbers from Rust takes 1 to 18% less time.
  • On x64, writing records with a short ASCII string of 16 to 32 bytes from Rust takes about 4 to 7% less time (a list of 1,000 records).
  • The TypeScript runtime (nino.ts) reads strings 16-25% faster in Node and mixed text 13-18% faster in Chromium, writes them 3-15% faster, and generated code writes maps 8-12% faster in Node (GitHub's EPYC 7763, 9V45, 9V74 and Neoverse-N2 runners).
  • Go: bool arrays are checked and copied eight bytes at a time: writing 1,000 bools is 88-91% faster and reading them 56-69% faster (EPYC 7763, EPYC 9V74 and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: arrays of fixed-size structs whose memory is their wire bytes (such as a Vec2 of two floats) are copied as one block: writing 1,000 is 90-93% faster and reading them 60-66% faster (EPYC 7763, EPYC 9V74 and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: arrays of DateTime, decimal, Guid and 128-bit integers are read and written as one block: an object with a DateTime[] (the .NET benchmark's SimpleClass) serializes 3.0-5.0% and deserializes 1.5-2.9% faster (EPYC 7763, EPYC 9V74 and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: struct tags read and write slices of bools, dates, decimals and Guids as one block: SimpleClass through tags serializes 2.4-6.2% and deserializes 6.6-7.6% faster, with one allocation fewer (EPYC 7763, EPYC 9V74 and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: sets and dictionaries of integer keys are checked for repeated keys with a sort instead of a map: reading a HashSet<int> of 1,000 is 51-63% faster (EPYC 7763, 9V45, 9V74, Xeon 6973P and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: struct tags read a []string of short ASCII strings 7.5-18.5% faster, and [NinoUtf8] strings are written 7.8-15.0% faster (1,000 strings; EPYC 7763, 9V45, 9V74, Xeon Platinum 8573C and Neoverse-N2 on GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Go: on Arm64, writing a []string of short strings is 8.3-8.4% faster (Neoverse-N2), and [NinoUtf8] strings are read 1.5-5.0% faster (EPYC 9V45, 9V74, Xeon Platinum 8573C and Neoverse-N2; GitHub's ubuntu-26.04 and ubuntu-26.04-arm runners).
  • Deserializing a new instance of a class with string members no longer keeps the reader in memory: SimpleClass 1.4-3.5% faster, version-tolerant TolerantClass 2.3-4.6%, a new instance of a pooled type 1.5-2.6% (AMD EPYC 7763 and Neoverse-N2).
  • Classes with string members deserialize 13 to 18% faster on x64 (AMD EPYC 7763) and Arm64 (Neoverse-N2): SimpleClass and the pooled benchmark's new objects, whose string members are read without keeping the reader in memory (previous commit) and whose one-char strings are no longer allocated.
  • SortedList<int, int> deserializes 21% faster on Arm64 (Neoverse-N2); SortedDictionary 3% faster, and ImmutableDictionary, ImmutableHashSet and SortedSet up to 3% faster, on x64 (AMD EPYC 7763) and Arm64.
  • Dictionary<string, string> deserializes 9% faster on x64 (AMD EPYC 7763) and 3% faster on Arm64 (Neoverse-N2).
  • Deserializing into existing objects (pooled or ref targets) whose long [NinoUtf8] text has not changed is 17 to 22% faster on x64 (AMD EPYC 7763) and Arm64 (Neoverse-N2) in the pooled benchmark.
  • Lists of fixed-size values read and write as one block. Reading 10,000 elements on GitHub's runners (AMD EPYC 7763 / Arm Neoverse-N2): bool 3.4 µs to 0.38 µs (−89% / −79%), Guid −88% / −86%, DateOnly −91% / −87%, DateTime −47% / −46%, decimal −25% / −24%, a raw struct −73% / −78%, (i32, i32) −86% / −85%, (i64, bool) −48% / −42%. Writing: bool and i8 −99% / −98%, a raw struct −75% / −80%, (i32, i32) −85% / −87%, (i64, bool) −80% / −76%, decimal −38% on EPYC 7763 (unchanged on Neoverse-N2).
  • Maps of fixed-size keys and values (numbers, value types, raw structs, tuples of them) serialize as one block in Rust: writing 1,000 (i32, i32) entries takes 44 to 65% less time on GitHub's runners (AMD EPYC 7763 −44%, EPYC 9V45 −48%, EPYC 9V74 −49%, Intel Xeon Platinum −51%, Arm Neoverse-N2 −65%).
  • Maps of fixed-size keys and values deserialize 1 to 6% faster in Rust on x64 (reading 1,000 (i32, i32) entries on GitHub's AMD EPYC 7763, 9V45 and 9V74 and Intel Xeon Platinum runners), and maps of 8-byte entries such as (i32, i32) 0.8% faster on Arm64 (Neoverse-N2).
  • The Go runtime reads and writes maps of primitive keys and values as one block (ReadPrimitiveMap, WritePrimitiveMap; struct tags too): SimpleClass serializes 5-11 % faster and deserializes 4-7 % faster with the generated-style codec, and 7-14 % / 2-6 % faster through struct tags (GitHub's AMD EPYC 7763, 9V45 and 9V74, Intel Xeon and Neoverse-N2 runners).
  • ConcurrentDictionary<int, int> deserializes 18 to 23% faster on x64 with AMD EPYC 7763 (on par on EPYC 9V45 and 9V74 and on Arm64).
  • Rust: writing strings that are neither ASCII nor Latin-1 is about 3.5 % faster on x64 CPUs with AVX2 but without AVX-512 (1,000 mixed strings, EPYC 7763).
  • On 32-bit .NET (x86, WebAssembly, Arm32) and 32-bit .NET Framework, values of 8 bytes or more are read and written with single unaligned loads and stores, and copies of up to 64 bytes no longer call memmove; Mono and IL2CPP on 32-bit Arm keep the byte copies. No GitHub runner covers 32-bit, so this is not measured.
  • On .NET Framework and .NET Core 3.0 to 7 (the netstandard builds), long Latin-1 strings are narrowed and widened with Vector<T> where the JIT accelerates it. On Mono, raw structs with padding no longer go through Vector<T>, which Mono 6.8 reports as accelerated but runs slower than the 8-byte path. Not measured on GitHub runners.
  • Rust: writing lists larger than the CPU's last-level cache whose elements hold strings, vectors or boxes is up to 4 times as fast (1,000,000 shuffled records: 3 to 4.6 times on EPYC and Neoverse-N2, 1.6 times on Xeon 8370C); cached lists are unchanged.
  • On Mono with the System.Numerics.Vectors package (the netstandard builds outside Unity), arrays of raw structs with padding are masked with Vector<byte> (SSE) again, faster than the 8-byte path. Not measured on GitHub runners.
  • Unity's Burst kernels are called through C# function pointers and run from 2 values for arrays and lists of tuples and structs (was 16) and from 16 chars for Latin-1 strings (was 32): writing 2 to 12 small tuples into a buffer is 1.2-3.4x and strings of 16 to 31 chars 1.2-1.7x as fast (IL2CPP, Linux x64 GitHub runner, Unity 2022.3 and 6000.3).
  • Unity WebGL players with WebAssembly SIMD write and read 1000-char Latin-1 strings 2.1-6x and write padded struct arrays of 1000 values 2.3-4.2x as fast (headless Chrome, Linux x64 GitHub runner).
  • Rust writes strings with non-Latin-1 text faster on AVX-512 CPUs: 1,000 mixed strings serialize in 6-9 % less time on GitHub's AMD EPYC 9V45 and Intel Xeon 8573C and 8370C runners, 3.5 % on EPYC 9V74.
  • Arrays of padded structs with a bool (1,000 Waypoints: two floats, an (int, short) and a bool) deserialize in 0.80 to 0.92 times the time (1,522 to 1,210 ns on EPYC 9V74, 1,874 to 1,718 ns on EPYC 7763, 2,553 to 2,288 ns on Neoverse-N2), and serialize 3.4 times as fast on Arm64 (1,438 to 427 ns on Neoverse-N2; GitHub runners).
  • Large arrays of raw values (10,000 Vector4, 160 KB) serialize 9.5% faster on Intel Xeon 8370C (3.51 to 3.18 µs) and deserialize 4.5-7.7% faster on AMD EPYC 9V45 and 9V74 (96.6 to 92.2 µs, 111.0 to 102.4 µs; GitHub runners).
  • Arrays of padded structs over 16 bytes (1,000 Waypoints: two floats, an (int, short) and a bool) serialize in 0.65 times the time on Intel Xeon 8573C (386 to 252 ns), 0.76 on AMD EPYC 9V45 (210 to 159 ns) and 0.97 on EPYC 9V74 (GitHub runners).
  • With Burst, the ASCII text of [NinoUtf8] strings is written 1.5 to 1.6 times as fast from 16 chars and 1.9 to 2.1 times at 1000, and read 1.06 to 1.24 times as fast from 16 chars and 1.2 to 1.25 times from 1000 (IL2CPP, Linux x64 GitHub runners, AMD EPYC 7763 and 9V74, Unity 2022.3 and 6000.3); WebGL players with SIMD write it 1.2 to 1.9 and read it 1.2 to 1.7 times as fast (headless Chrome). CJK text is on par.
  • Members of generic [NinoType]s and of [NinoType]s generated in another assembly are called directly instead of through a virtual call: a generic type holding an unmanaged tuple deserializes in 0.86-0.94 of the time on GitHub's AMD EPYC 7763, 9V45 and 9V74 and Neoverse-N2 runners and serializes 2-4% faster on the 7763, 9V74 and Neoverse-N2 (noise floor 0.99-1.01).
  • HashSet<T> and Dictionary<TKey, TValue> of unmanaged values serialize their entries several at a time with SIMD shuffles: a HashSet<int> of 1,000 in 0.34-0.49 times the time (327 to 141 ns on EPYC 7763, 372 to 126 ns on Neoverse-N2), a Dictionary<int, int> of 1,000 in 0.29-0.56 times (430 to 212 ns on EPYC 7763, 369 to 107 ns on Xeon Platinum 8573C), and the polymorphic class array, whose members hold such collections, in 0.69-0.84 times (GitHub's EPYC 7763, 9V45, 9V74, Xeon Platinum 8573C and 8370C, Xeon 6973P and Neoverse-N2 runners).
  • In NinoSettings.Untrusted mode, the values checked in arrays and lists of raw structs are checked without a branch per value, and with SIMD on .NET 8+: a list of 1,000 structs with a DateTime and a bool deserializes in 0.47-0.69 times the time (5.55 to 2.76 µs on EPYC 7763, 6.13 to 2.86 µs on Neoverse-N2; GitHub runners).
  • On Unity and .NET Framework (the netstandard builds), reading a Latin-1 or ASCII string into an instance that holds one of the same length compares them eight bytes at a time instead of one char at a time; no GitHub runner covers these platforms, so there are no numbers.
  • Rust lists of plain structs whose members are all fixed-size (#[derive(NinoRead, NinoWrite)] or nino-gen) are read and written as one block: 10,000 { i32, f32 } read 80-91% and write 85-88% faster, 10,000 { i64, bool, i32 } read 61-78% and write 63-88% faster on GitHub's AMD EPYC 7763, 9V45, 9V74, Intel Xeon 8573C, 8370C and Neoverse-N2/V3 runners.
  • Go code nino-gen generates calls each element of a collection directly instead of through a function value: on GitHub's runners, a class with a dictionary and lists serializes 3-5% faster (AMD EPYC 9V45 and 9V74, Intel Xeon 8370C and 6973P-C, Neoverse-N2 and V3) and a list of 1,000,000 records reads up to 8% faster (EPYC 7763).
  • Go ReadBlock/WriteBlock of DateTime, TimeOnly and decimal, and ReadRaw/WriteRaw of fixed-size structs with a bool, check their values four at a time: writing 1,000 values is 29-38% faster for DateTime, 2-25% for decimal, 9-24% for TimeOnly and 9-39% for an 8-byte struct with a bool, and reading them 3-14% faster (GitHub's AMD EPYC 7763, 9V45 and 9V74, Intel Xeon Platinum 8573C and 8370C, Neoverse-N2).
  • Go Reader.UTF16 reads 1,000 wide code units 60-63% faster and Writer.UTF16 writes them 92-96% faster; Latin-1 code units are written 40-64% faster (GitHub's AMD EPYC 7763 and 9V74, Intel Xeon Platinum 8370C, Neoverse-N2).
  • With NinoPrefetchLargeCollections, writing a million shuffled objects is 2.0-4.8x as fast and reading into them 2.0-3.6x (GitHub runners, EPYC 7763/9V45/9V74 and Neoverse-N2); short top-level collections can be up to 6 % slower.
  • Generated Go calls class bodies directly: on GitHub's runners a list of 1,000,000 records reads 4-7% faster (AMD EPYC 7763, 9V45, 9V74, Neoverse-N2) and writes 2-6% faster than before the collection change (EPYC, Xeon 8573C, Neoverse-N2).
  • 1,000 padded 24-byte structs with a bool (the benchmarks' Waypoint) deserialize in 0.77 to 0.85 times the time on GitHub's AMD EPYC 7763 and 9V74, Intel Xeon Platinum 8370C and Neoverse-N2 runners (EPYC 9V74: 1,203 to 922 ns), and a list of 1,000 (int, long) in 0.97 to 0.99 (A/B run 40935400095).
  • Arrays of padded raw structs serialize about 1.4 times as fast on Intel Ice Lake (GitHub's Xeon Platinum 8370C: 1,000 8-byte structs with a padding byte in 83 ns instead of 116, MemoryPack 88); other CPUs are unchanged.
  • TypeScript deserialize reads a struct member into the default instance its class already holds instead of making a second one: Mesh (arrays of Vec3 structs) deserializes 8 to 24 % faster on GitHub runners (A/B 40969410249), other cases on par.
  • In Unity's IL2CPP players, arrays and lists of tuples and fixed-size structs no longer allocate for each element when no Burst kernel handles them, and top-level Serialize calls no longer allocate before writing (the netstandard builds box each formatter once).
  • Members of type T in generic [NinoType]s are direct calls on every runtime (IL2CPP, Mono and Native AOT made a virtual call per value): on .NET, generic polymorphic arrays read and write on par or faster on GitHub's x64 and Arm64 runners (serialize up to 11% faster on AMD EPYC 9V74), except an array of a generic base over a (class, int) tuple, which deserializes 4-5% slower on Xeon 8573C (Emerald Rapids).
  • Go: sets read from the data are checked for a repeated element in a seeded table instead of a sort or a map, and sets of numbers are read as one block: a HashSet<int> of 1,000 reads in 0.16-0.19 of the time and a HashSet<string> of 1,000 in 0.54-0.66 (GitHub's AMD EPYC and Neoverse-N2 runners). Regenerate Go code to take it.
  • Rust: a sealed class's generated read and write methods are #[inline], as the derive's were: a list of 1,000 records reads 2.0-4.3% and writes 2.0-9.2% faster (GitHub's AMD EPYC and Neoverse-N2 runners). Regenerate Rust code to take it.
  • Arrays of padded structs serialize 1.2 times as fast on Intel Xeon Platinum 8573C (Emerald Rapids), whose cloud VMs hide AVX512-FP16: 1,000 Waypoints in 259 ns instead of 336 and 1,000 8-byte structs with a padding byte in 65 ns instead of 83 (GitHub runners); Ice Lake keeps its 32-byte kernels.
  • Arrays of padded structs with a bool (such as Waypoint) deserialize 4-8% faster on x64 CPUs without AVX-512, the bools checked in the same pass (GitHub's AMD EPYC 7763: 1,000 Waypoints in 1,240 ns instead of 1,360).
  • On Intel Ice Lake (Xeon Platinum 8370C), lists and arrays of padded fixed-size values such as (int, long) and Dictionary<int, int> entries are written from cache-line-aligned loads: 1,000 (int, long) serialize in 0.92 of the time (235 to 221 ns) and a 1,000-entry Dictionary<int, int> in 0.81 (250 to 200 ns). Other CPUs are unchanged.
  • On Intel Ice Lake under Linux (Xeon Platinum 8370C), raw blocks of 2 to 32 KB (unpadded structs, KeyValuePair<int, int>, primitives) are copied with the C library's rep movsb: 1,000 KeyValuePair<int, int> serialize in 0.85 of the time (151 to 128 ns) and a 1,000-int Queue<int> in 0.78 (94 to 74 ns). Other CPUs are unchanged.
  • Rust on Arm64 writes short strings that are not ASCII faster: 1,000 mixed strings serialize in 7.7 % less time on GitHub's Neoverse-N2 runners, and lists of 16- to 24-char Latin-1, Cyrillic, CJK and mixed strings in 4-13 % less.
  • Collections that hold themselves without a [NinoType] body (class Tree : List<Tree>, class Map : Dictionary<string, Map>) call each level directly instead of through a virtual call: a 5,461-list tree serializes in 0.35-0.42x and deserializes in 0.52-0.58x the time on x64 and Arm64 runners.
  • Reading a CultureInfo no longer asks ICU each time for a name it already accepted: 0.43-0.57 of v4.0.0-rc.7's time and 0.05 of the previous build's on every runner CPU (.NET A/B runs 41455371503, 41534216283).
  • A TimeZoneInfo of a system zone reads its null rules inline: 0.83-0.92 of the previous build's time, 0.89-0.98 of v4.0.0-rc.7's (.NET A/B run 41534216283).
  • Strings read again into existing objects (pooled or reused instances) are compared without a second call: 1,000,000 shuffled items read into existing objects in 0.53-0.86 of the previous build's time and 0.56-0.95 of v4.0.0-rc.7's (.NET A/B runs 41534216283, 41576130603).
  • Reading into existing objects (pooled or reused instances) no longer stores back a string member that did not change: NinoDeserializePooled in 0.86-0.97 of v4.0.0-rc.7's time on Neoverse-N2 and Xeon 6975P and 8573C (.NET A/B run 41621699299), 0.89-0.93 of the previous build's on every CPU.

Fixes

  • TypeScript reads and writes values nested deeper than 128 class bodies (linked lists, trees) byte for byte as .NET does, and deeply nested or forged data throws a NinoError instead of overflowing the stack.
  • Reading a [NinoUtf8] string whose bytes are not UTF-8 in TypeScript throws a NinoError instead of a TypeError.
  • Deserializing a Unity Gradient from data with -1 as its alpha key count throws Nino's InvalidOperationException instead of OverflowException.
  • With Nino.AspNetCore, a collectible AssemblyLoadContext whose types went through the MVC formatters can unload again.
  • Editing an [assembly: NinoFormatter(...)] registration in the IDE now updates the generated formatters that use it, instead of keeping the old ones until the next rebuild.
  • Top-level NinoSerializer/NinoDeserializer calls written through a using alias or using static are now intercepted and their formatters registered for AOT, like calls that name the class.
  • The source generator no longer keeps the cached models and syntax trees of closed projects in memory, which grew the IDE's (and build servers') memory over a long session.
  • The published layouts (NinoTypeLayoutAttribute) of Half, Rune, DateOnly, TimeOnly, Index, Range, Complex, Int128, UInt128 and arrays of nullable values now show the bytes Nino writes instead of the placeholders of reference assemblies.
  • Members (and elements) that are tuples of eight items or more are now serialized instead of being skipped with warning NINO007.
  • Projects with two [NinoType]s whose names differ only by case (RawS and Raws) get their generated code again; the generator failed with CS8785 and added none.
  • An assembly removed with NinoRegistry.Remove (or by its collectible AssemblyLoadContext unloading) is no longer registered again by a later read of an unknown type id, which kept it from unloading; an assembly loaded right after another one unloaded is now found by reads by type id.
  • NinoSerializer.SerializeBoxed(null, type) for a value type throws ArgumentNullException instead of a NullReferenceException.
  • On a big-endian host, raw values, arrays of them and fixed-size types serialized through the top-level calls are rejected like every other type, instead of being written in the host's byte order.
  • Reading data that nests a collection type holding itself (class Tree : List<Tree>) deeper than the depth limit throws InvalidOperationException instead of overflowing the stack, and the runtime-built formatter of such a type no longer overflows the stack while it is created.
  • Reading the same unknown type id again (corrupt, hostile or stale data) no longer searches every registry and loaded assembly each time; the id is found as soon as an assembly declaring it is loaded or registered.
  • Under NativeAOT, generic unmanaged structs that are not [NinoType]s serialize as raw values, as they do with the JIT and in generated code, instead of failing for lack of a formatter.
  • NinoSubtypeHandler can no longer be subclassed, nor NinoPolymorphic<TBase>.Register(NinoSubtypeHandler) called, outside Nino.Core: only generated registrations add polymorphic subtypes.
  • Generated TypeScript for a [NinoType] without members compiles with noUnusedParameters on (its empty read and write bodies name their parameters _r, _w and _v).
  • Reading data Nino never writes now throws NinoDataException in every mode: a bool or null/presence byte other than 0 or 1, a keyed member whose entry holds more bytes than its value, and a version-tolerant deferred section whose offset or entries do not match its region. Bools inside raw structs and tuples are still copied as they are.
  • Raw keys read into an existing Dictionary<TKey, TValue> with a custom comparer are now hashed by that comparer; before, lookups could miss them.
  • Serializing an object graph nested deeper than NinoSettings.MaxDepth through structs or collections that hold themselves (or one that contains itself) now throws InvalidOperationException instead of overflowing the stack.
  • The TypeScript runtime now rejects data .NET rejects: a repeated key or element in a map or set (of numbers, strings, bigints, booleans or null), a keyed member whose value leaves bytes in its entry, and a malformed section of deferred bodies, each with a NinoError.
  • The Go and Rust readers accept a DateTime that .NET wrote for a local time in the repeated hour of a daylight saving change (kind 3), instead of failing with invalid data.
  • A closed generic [NinoType] struct was written in two encodings, raw inside other generated types and member by member through a generic type or a top-level call, so one could not read what the other wrote.
  • Value tuples of eight items or more can be the type argument of a generic [NinoType] that generated code never sees closed; Nino.Core threw "no formatter" for them.
  • Writing an object graph that nests structs holding themselves around records (or other classes Nino cannot defer) now throws InvalidOperationException once the two together pass NinoSettings.MaxDepth, instead of writing data its own reader rejects.
  • Reading a ConcurrentDictionary now enforces NinoSettings.MaxHashCollisions like the other hash tables: keys built to share a hash bucket throw NinoDataException instead of making the read take quadratic time.
  • A closed generic subtype held in a slot of a closed generic type (ListShelf<string> in a Shelf<string> member) is now read in a process that never wrote or named it, instead of failing as an unregistered subtype.
  • Reading a [NinoType] collection whose element count marks it null throws NinoDataException instead of NullReferenceException.
  • On Intel Macs with AVX-512 (macOS 12.2 and later), the Go runtime's string writer and reader now take their AVX-512 kernels; they took the AVX2 ones.
  • ASP.NET Core answers 400 Bad Request only for request bodies that are not valid Nino data (NinoDataException); a failure on the server's side while reading one, such as a [NinoFactory] that returns null, is no longer reported as the client's fault.
  • Unity's RectOffset now rejects a presence byte other than 0 or 1 as invalid data (NinoDataException), as every other nullable value does, instead of reading it as present.
  • The nino-serialization npm package exports its version as ninoVersion, instead of the placeholder "__NINO_VERSION__".
  • The nino-serialization npm package declares the Node versions it runs on (engines: 15 and later), instead of 18.
  • The TypeScript runtime rejects a decimal whose scale exceeds 28 or whose flags have other bits set, as the Go and Rust runtimes do, instead of reading a value it cannot write back.
  • The TypeScript runtime rejects a DateTime whose ticks exceed DateTime.MaxValue's, as the Go and Rust runtimes do, instead of reading a date it cannot write back.
  • Under NativeAOT on x64, arrays and lists of value tuples, KeyValuePairs and fixed-size structs whose memory differs from their bytes (such as (int, long)) are moved by the SSSE3 shuffle instead of its software fallback, which in a local run made writing 1,000 (int, long) about 13 times as slow (25 us against 1.9 us with the fix) and reading them about 7 times as slow. The AVX2 and AVX-512 paths still need IlcInstructionSet there.
  • Rust: #[derive(NinoRead)] no longer rejects a list of structs with a #[nino(utf8)] member holding short strings (InvalidLength).
  • NINO007 now also reports a member whose type closes a generic [NinoType] over a type Nino cannot write (Box<Action>, also inside collections, arrays and other generics), where writing any value threw at run time; the bytes do not change.
  • Reading invalid data into an existing value (Deserialize(data, ref value)) no longer leaves it holding a new array of uninitialized memory when the data was rejected after the array was allocated: raw T? arrays and lists, bool lists, collections of block structs with a bool such as (int, bool)[], and checked values in Untrusted mode.
  • Go: sets and dictionaries read from the data reject a second NaN element or key, as .NET does; they accepted it.
  • TypeScript: a map or set of more than 128 number or bigint keys read from untrusted data can no longer be made quadratic by keys chosen to share one of V8's hash buckets; such data throws a NinoError, as .NET's MaxHashCollisions does.
  • TypeScript: a map or set of more than 128 string keys read from untrusted data can no longer be made quadratic by keys that are array indices ("0", "2048", ...), which V8 hashes by their value without a seed; such data throws a NinoError, as number and bigint keys already did.
  • On .NET 8 and later, a CultureInfo read from untrusted data can no longer grow the process's memory without bound (.NET keeps every culture it creates): only cultures the runtime ships read back, including aliases such as zh-CN; custom cultures and names with -u- or -x- extensions are now invalid data (NinoDataException), and in invariant globalization mode only the invariant culture reads back. .NET Framework, Mono and Unity are unchanged.
  • Reading a Unity Gradient whose data claims more than 8 color or alpha keys now throws NinoDataException before allocating, instead of an exception from Unity's setters.
  • Under Native AOT, a closed generic [NinoType] named only in NinoSerializer.GetFixedSize<T>() or as the typeof argument of SerializeBoxed, DeserializeBoxed or IsSupported works as on the JIT; the generator registered only the type arguments of Serialize and Deserialize, and such calls threw "no formatter".
  • On 32-bit x86 .NET, a struct written as raw bytes that holds a DateTime after a smaller field (such as struct { int A; DateTime B; }) is converted to the canonical layout instead of being copied as its 12-byte x86 memory, which other runtimes could not read.
  • On .NET Framework, a struct written as raw bytes that holds a DateTime after a smaller field and before another (such as struct { byte A; DateTime B; int C; }) is written in the canonical layout instead of its reordered memory, which other runtimes could not read and which the 32-bit build could not read back.

Every change between v4 releases: Changelog.

What's Changed since v4.0.0-rc.7

806 commits

⚠️ Breaking changes

✨ Features (53)
⚡ Performance (148)
🐛 Fixes (86)

…truncated; see the full changelog: v4.0.0-rc.7...v4.0.0-rc.8

Contributors

Thanks to everyone who contributed to this release: @JasonXuDeveloper

Full Changelog: v4.0.0-rc.7...v4.0.0-rc.8

📊 v3 3.3.13 vs v4 (same machine)

Nino v3 3.3.13 (NuGet) against v4.0.0-rc.8, back to back on the same runners

The v3 build of benchmarks/Nino.Benchmark (the Nino and MemoryPack cases of the same SimpleTest, built against Nino 3.3.13 from nuget.org) ran directly before the v4
SimpleTest run below, its serialize and its deserialize cases each on the runner of the same v4 cases, with
the same BenchmarkDotNet settings
(--launchCount 3 --warmupCount 5 --iterationCount 15). MemoryPack runs in both, so its two columns show how much the runner moved.

v4 vs v3

case v3 3.3.13 v4 v4 vs v3 MemoryPack (v4 run) MemoryPack (v3 run)
SerializeSimpleClass 185.39 ns 93.58 ns 0.50x 389.82 ns 397.80 ns
SerializeSimpleClasses 24.06 μs 10.15 μs 0.42x 40.68 μs 39.83 μs
SerializeSimpleStruct 3.46 ns 4.52 ns 1.31x 5.11 ns 5.14 ns
SerializeSimpleStructs 22.34 ns 18.32 ns 0.82x 22.21 ns 23.14 ns
SerializeVectors 3.31 μs 3.26 μs 0.98x 3.27 μs 3.33 μs
DeserializeSimpleClass 599.27 ns 392.62 ns 0.66x 804.52 ns 797.51 ns
DeserializeSimpleClasses 65.05 μs 44.21 μs 0.68x 80.79 μs 79.23 μs
DeserializeSimpleStruct 0.70 ns 0.71 ns on par (both too fast to measure) 1.45 ns 1.47 ns
DeserializeSimpleStructs 92.41 ns 85.79 ns 0.93x 101.99 ns 109.84 ns
DeserializeVectors 126.81 μs 124.33 μs 0.98x 125.10 μs 126.51 μs

v4 vs v3 = mean(v4) / mean(v3); below 1.00x means v4 is faster.
MemoryPack drift (v4 run / v3 run): min 0.93x, max 1.02x (same code in both runs; the spread is the noise floor of this comparison).

v3 3.3.13 runners


BenchmarkDotNet v0.15.8, Linux Ubuntu 26.04.1 LTS (Resolute Raccoon)
AMD EPYC 7763 3.21GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.403
  [Host]             : .NET 10.0.14 (10.0.14, 10.0.1426.52326), X64 RyuJIT x86-64-v3
  ShortRun-.NET 10.0 : .NET 10.0.14 (10.0.14, 10.0.1426.52326), X64 RyuJIT x86-64-v3

Job=ShortRun-.NET 10.0  Runtime=.NET 10.0  Server=True  
IterationCount=15  LaunchCount=3  WarmupCount=5  

The cases ran in parallel on several runners (every serializer's run of a case on one):

  • SimpleTest (NinoSerializeSimpleClass to MemoryPackSerializeVectors): AMD EPYC 7763 3.21GHz, 1 CPU, 4 logical and 2 physical cores
  • SimpleTest (NinoDeserializeSimpleClass to MemoryPackDeserializeVectors): AMD EPYC 7763 3.15GHz, 1 CPU, 4 logical and 2 physical cores

The v3 report: benchmark-v3-full.md.

📊 Performance Report

Click to expand benchmark results

BenchmarkDotNet v0.15.8, Linux Ubuntu 26.04.1 LTS (Resolute Raccoon)
AMD EPYC 7763 2.72GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.403
  [Host]             : .NET 10.0.14 (10.0.14, 10.0.1426.52326), X64 RyuJIT x86-64-v3
  ShortRun-.NET 10.0 : .NET 10.0.14 (10.0.14, 10.0.1426.52326), X64 RyuJIT x86-64-v3

Job=ShortRun-.NET 10.0  Runtime=.NET 10.0  Server=True  
IterationCount=15  LaunchCount=3  WarmupCount=5  

ReferenceTest

Method Mean Ratio Payload Allocated
NinoDeserializeGraph 6.10 μs 1.00 - 14104 B
MemoryPackDeserializeGraph 20.10 μs 3.30 - -
NinoSerializeGraph 5.99 μs 1.00 5,830 B -
MemoryPackSerializeGraph 30.04 μs 5.02 5,584 B -

PooledTest

Method Mean Ratio Payload Allocated
NinoDeserializeNew 382.03 ns 1.00 - 2296 B
NinoDeserializePooled 232.04 ns 0.61 - -
MemoryPackDeserializeNew 732.56 ns 1.92 - 2536 B
MessagePackDeserializeNew 1.78 μs 4.67 - 2536 B
ProtobufDeserializeNew 6.10 μs 15.98 - 3544 B
MemoryPackDeserializeOverwrite 607.95 ns 1.59 - 1080 B

SimpleTest

Method Mean Ratio Payload Allocated
NinoSerializeSimpleClass 93.58 ns 1.00 1,069 B -
MemoryPackSerializeSimpleClass 389.82 ns 4.17 1,148 B -
MessagePackSerializeSimpleClass 1.11 μs 11.91 1,068 B -
ProtobufSerializeSimpleClass 3.78 μs 40.43 1,257 B 80 B
NinoSerializeSimpleClasses 10.15 μs 1.00 106,904 B -
MemoryPackSerializeSimpleClasses 40.68 μs 4.01 114,804 B -
MessagePackSerializeSimpleClasses 111.92 μs 11.03 106,803 B -
ProtobufSerializeSimpleClasses 371.69 μs 36.64 125,460 B 8000 B
NinoSerializeSimpleStruct 4.52 ns 1.00 12 B -
MemoryPackSerializeSimpleStruct 5.11 ns 1.13 12 B -
MessagePackSerializeSimpleStruct 57.83 ns 12.79 12 B -
ProtobufSerializeSimpleStruct 157.53 ns 34.83 16 B -
NinoSerializeSimpleStructs 18.32 ns 1.00 1,204 B -
MemoryPackSerializeSimpleStructs 22.21 ns 1.21 1,204 B -
MessagePackSerializeSimpleStructs 3.56 μs 194.26 1,203 B -
ProtobufSerializeSimpleStructs 10.27 μs 560.56 1,784 B -
NinoSerializeVectors 3.26 μs 1.00 160,004 B -
MemoryPackSerializeVectors 3.27 μs 1.01 160,004 B -
MessagePackSerializeVectors 165.17 μs 50.72 210,003 B -
ProtobufSerializeVectors 605.85 μs 186.06 220,000 B -
NinoDeserializeSimpleClass 392.62 ns 1.00 - 2288 B
MemoryPackDeserializeSimpleClass 804.52 ns 2.06 - 2528 B
MessagePackDeserializeSimpleClass 1.86 μs 4.74 - 2528 B
ProtobufDeserializeSimpleClass 6.43 μs 16.42 - -
NinoDeserializeSimpleClasses 44.21 μs 1.00 - 229624 B
MemoryPackDeserializeSimpleClasses 80.79 μs 1.83 - 253624 B
MessagePackDeserializeSimpleClasses 200.87 μs 4.55 - 253624 B
ProtobufDeserializeSimpleClasses 630.10 μs 14.27 - 354424 B
NinoDeserializeSimpleStruct 0.71 ns 1.00 - -
MemoryPackDeserializeSimpleStruct 1.45 ns 2.05 - -
MessagePackDeserializeSimpleStruct 77.63 ns 109.70 - -
ProtobufDeserializeSimpleStruct 226.82 ns 320.54 - -
NinoDeserializeSimpleStructs 85.79 ns 1.00 - 1224 B
MemoryPackDeserializeSimpleStructs 101.99 ns 1.19 - 1224 B
MessagePackDeserializeSimpleStructs 3.51 μs 41.00 - 1224 B
ProtobufDeserializeSimpleStructs 10.32 μs 120.48 - 1224 B
NinoDeserializeVectors 124.33 μs 1.00 - 160033 B
MemoryPackDeserializeVectors 125.10 μs 1.01 - 160031 B
MessagePackDeserializeVectors 391.42 μs 3.15 - 160035 B
ProtobufDeserializeVectors 715.11 μs 5.76 - 160041 B

FixedSizeTest

Method Mean Ratio Payload Allocated
NinoDeserializeFixedClass 10.41 ns 1.00 - 56 B
MemoryPackDeserializeFixedClass 22.61 ns 2.18 - 56 B
MessagePackDeserializeFixedClass 174.03 ns 16.74 - 56 B
ProtobufDeserializeFixedClass 308.66 ns 29.69 - 56 B
NinoSerializeFixedClass 10.22 ns 1.00 41 B 72 B
MemoryPackSerializeFixedClass 26.25 ns 2.57 41 B 72 B
MessagePackSerializeFixedClass 120.39 ns 11.79 69 B -
ProtobufSerializeFixedClass 283.01 ns 27.71 53 B 80 B
NinoDeserializeFixedClasses 1.16 μs 1.00 - 6424 B
MemoryPackDeserializeFixedClasses 1.39 μs 1.20 - 6424 B
MessagePackDeserializeFixedClasses 25.54 μs 21.99 - 6424 B
ProtobufDeserializeFixedClasses 19.23 μs 16.56 - -
NinoSerializeFixedClasses 467.38 ns 1.00 4,104 B 4128 B
MemoryPackSerializeFixedClasses 1.82 μs 3.90 4,104 B 4128 B
MessagePackSerializeFixedClasses 8.34 μs 17.86 6,903 B -
ProtobufSerializeFixedClasses 20.97 μs 44.87 5,488 B -
NinoDeserializeFixedStruct 0.40 ns 1.12 - -
MemoryPackDeserializeFixedStruct 0.97 ns 2.74 - -
MessagePackDeserializeFixedStruct 175.84 ns 496.73 - -
ProtobufDeserializeFixedStruct 275.23 ns 777.48 - -
NinoSerializeFixedStruct 9.30 ns 1.00 40 B 64 B
MemoryPackSerializeFixedStruct 9.61 ns 1.04 40 B 64 B
MessagePackSerializeFixedStruct 115.75 ns 12.47 69 B -
ProtobufSerializeFixedStruct 268.95 ns 28.98 53 B 80 B

ScatteredTest

Method Mean Ratio Payload Allocated
NinoSerializeHotShapes 16.82 μs 1.00 114,692 B -
MemoryPackSerializeHotShapes 201.16 μs 11.96 98,308 B -
MessagePackSerializeHotShapes 336.70 μs 20.01 110,211 B -
ProtobufSerializeHotShapes 1.09 ms 64.72 126,841 B -
NinoSerializeInOrderShapes 3.06 ms 1.00 14,000,004 B -
MemoryPackSerializeInOrderShapes 25.28 ms 8.28 12,000,004 B -
MessagePackSerializeInOrderShapes 37.05 ms 12.13 15,368,549 B -
ProtobufSerializeInOrderShapes 137.56 ms 45.04 16,483,481 B -
NinoDeserializeScatteredItemsInto 64.17 ms 1.00 - -
MemoryPackDeserializeScatteredItemsInto 153.32 ms 2.39 - 47920000 B
NinoSerializeScatteredItems 91.84 ms 1.00 24,888,894 B -
MemoryPackSerializeScatteredItems 249.70 ms 2.72 31,888,894 B -
MessagePackSerializeScatteredItems 244.07 ms 2.66 22,738,657 B -
ProtobufSerializeScatteredItems 306.14 ms 3.33 23,570,420 B -
NinoSerializeScatteredShapes 22.71 ms 1.01 14,000,004 B -
MemoryPackSerializeScatteredShapes 138.23 ms 6.14 12,000,004 B -
MessagePackSerializeScatteredShapes 148.00 ms 6.58 15,368,549 B -
ProtobufSerializeScatteredShapes 245.60 ms 10.91 16,483,481 B -

PooledGraphTest

Method Mean Ratio Payload Allocated
NinoLeavesNew 3.04 μs 1.00 - 13656 B
NinoLeavesPooledElements 3.16 μs 1.04 - 856 B
NinoLeavesInPlace 469.66 ns 0.15 - -
MemoryPackLeavesNew 3.87 μs 1.27 - 13656 B
MessagePackLeavesNew 10.01 μs 3.29 - -
ProtobufLeavesNew 31.25 μs 10.27 - -
MemoryPackLeavesOverwrite 5.12 μs 1.68 - 12800 B
NinoNodeNew 474.97 ns 1.00 - 2192 B
NinoNodePooled 157.06 ns 0.33 - -
MemoryPackNodeNew 688.48 ns 1.45 - 2192 B
MessagePackNodeNew 1.88 μs 3.97 - -
ProtobufNodeNew 6.41 μs 13.51 - -
MemoryPackNodeOverwrite 621.78 ns 1.31 - 1024 B
NinoPlainNew 3.18 μs 1.00 - 12856 B
MemoryPackPlainNew 4.22 μs 1.33 - 12856 B
MessagePackPlainNew 10.96 μs 3.45 - 12856 B
ProtobufPlainNew 31.58 μs 9.95 - 16088 B

UntrustedTest

Method Mean Ratio Payload Allocated
NinoDeserializeUntrustedList 2.65 μs 1.00 - 24056 B
MemoryPackDeserializeUntrustedList 1.70 μs 0.64 - 24056 B

BclTypesTest

Method Mean Ratio Payload Allocated
NinoDeserializeBigInteger 35.61 ns 1.00 - 256 B
MessagePackDeserializeBigInteger 104.96 ns 2.95 - 256 B
NinoSerializeBigInteger 13.43 ns 1.00 203 B -
MessagePackSerializeBigInteger 39.76 ns 2.96 201 B -
NinoDeserializeBitArray 50.94 ns 1.03 - 336 B
MessagePackDeserializeBitArray 4.04 μs 82.01 - 184 B
NinoSerializeBitArray 25.69 ns 1.00 129 B -
MessagePackSerializeBitArray 2.37 μs 92.15 1,003 B -
NinoDeserializeCultureInfo 15.90 ns 1.00 - 32 B
MemoryPackDeserializeCultureInfo 41.68 ns 2.62 - 64 B
NinoSerializeCultureInfo 2.88 ns 1.00 6 B -
MemoryPackSerializeCultureInfo 15.22 ns 5.29 13 B -
NinoDeserializeTimeZoneInfo 7.92 ns 1.00 - 32 B
MemoryPackDeserializeTimeZoneInfo 187.22 ns 23.65 - 680 B
NinoSerializeTimeZoneInfo 3.72 ns 1.00 5 B -
MemoryPackSerializeTimeZoneInfo 109.09 ns 29.31 102 B 216 B
NinoDeserializeBlockingCollection 5.33 μs 1.00 - 12792 B
MemoryPackDeserializeBlockingCollection 29.19 μs 5.47 - 17984 B
NinoSerializeBlockingCollection 5.38 μs 1.00 4,004 B 4096 B
MemoryPackSerializeBlockingCollection 11.85 μs 2.20 4,004 B 72 B
NinoDeserializeImmutableDictionary 21.13 μs 1.00 - 68088 B
MemoryPackDeserializeImmutableDictionary 112.38 μs 5.32 - 56096 B
MessagePackDeserializeImmutableDictionary 121.53 μs 5.76 - 56097 B
ProtobufDeserializeImmutableDictionary 164.54 μs 7.80 - 56080 B
NinoSerializeImmutableDictionary 3.36 μs 1.00 8,004 B -
MemoryPackSerializeImmutableDictionary 20.05 μs 5.97 8,004 B -
MessagePackSerializeImmutableDictionary 27.17 μs 8.09 5,563 B -
ProtobufSerializeImmutableDictionary 73.47 μs 21.87 7,849 B 208 B
NinoDeserializeImmutableHashSet 21.30 μs 1.00 - 64096 B
MemoryPackDeserializeImmutableHashSet 106.27 μs 5.00 - 56104 B
MessagePackDeserializeImmutableHashSet 108.21 μs 5.09 - -
ProtobufDeserializeImmutableHashSet 118.41 μs 5.57 - 56120 B
NinoSerializeImmutableHashSet 3.07 μs 1.00 4,004 B -
MemoryPackSerializeImmutableHashSet 26.26 μs 8.56 4,004 B -
MessagePackSerializeImmutableHashSet 18.62 μs 6.07 2,619 B -
ProtobufSerializeImmutableHashSet 44.20 μs 14.41 2,872 B 168 B
NinoDeserializeImmutableQueue 8.04 μs 1.00 - 36064 B
MemoryPackDeserializeImmutableQueue 9.90 μs 1.23 - 32112 B
MessagePackDeserializeImmutableQueue 22.98 μs 2.86 - 72024 B
ProtobufDeserializeImmutableQueue 35.66 μs 4.44 - 72000 B
NinoSerializeImmutableQueue 1.95 μs 1.00 4,004 B -
MemoryPackSerializeImmutableQueue 8.58 μs 4.41 4,004 B -
MessagePackSerializeImmutableQueue 6.74 μs 3.46 2,619 B 48 B
ProtobufSerializeImmutableQueue 14.64 μs 7.51 2,872 B -
NinoDeserializeImmutableSortedSet 13.71 μs 1.00 - 52056 B
MemoryPackDeserializeImmutableSortedSet 90.38 μs 6.60 - 48089 B
MessagePackDeserializeImmutableSortedSet 90.40 μs 6.60 - 48088 B
ProtobufDeserializeImmutableSortedSet 36.29 μs 2.65 - 52120 B
NinoSerializeImmutableSortedSet 2.81 μs 1.00 4,004 B -
MemoryPackSerializeImmutableSortedSet 14.27 μs 5.09 4,004 B -
MessagePackSerializeImmutableSortedSet 10.81 μs 3.85 2,619 B -
ProtobufSerializeImmutableSortedSet 24.89 μs 8.87 2,872 B 168 B
NinoDeserializeImmutableStack 7.99 μs 1.00 - 36024 B
MemoryPackDeserializeImmutableStack 10.30 μs 1.29 - 32040 B
MessagePackDeserializeImmutableStack 13.44 μs 1.69 - 36024 B
ProtobufDeserializeImmutableStack 25.47 μs 3.19 - 32000 B
NinoSerializeImmutableStack 2.95 μs 1.00 4,004 B -
MemoryPackSerializeImmutableStack 15.64 μs 5.32 4,004 B 4112 B
MessagePackSerializeImmutableStack 5.87 μs 2.00 2,619 B 40 B
ProtobufSerializeImmutableStack 15.54 μs 5.29 2,872 B -
NinoDeserializeImmutableSortedDictionary 14.07 μs 1.00 - 56072 B
MemoryPackDeserializeImmutableSortedDictionary 76.21 μs 5.42 - 48112 B
MessagePackDeserializeImmutableSortedDictionary 79.90 μs 5.69 - 48112 B
ProtobufDeserializeImmutableSortedDictionary 140.46 μs 10.00 - 104520 B
NinoSerializeImmutableSortedDictionary 3.53 μs 1.00 8,004 B -
MemoryPackSerializeImmutableSortedDictionary 8.90 μs 2.52 8,004 B -
MessagePackSerializeImmutableSortedDictionary 12.59 μs 3.57 5,563 B -
ProtobufSerializeImmutableSortedDictionary 41.43 μs 11.75 7,849 B 208 B
NinoDeserializeLazy 20.28 ns 1.00 - 112 B
MemoryPackDeserializeLazy 35.47 ns 1.75 - -
MessagePackDeserializeLazy 86.84 ns 4.29 - 232 B
NinoSerializeLazy 4.65 ns 1.00 24 B -
MemoryPackSerializeLazy 31.91 ns 6.86 31 B -
MessagePackSerializeLazy 38.75 ns 8.33 23 B -
NinoDeserializeMemory 275.48 ns 1.00 - 4056 B
MemoryPackDeserializeMemory 327.22 ns 1.19 - 4056 B
MessagePackDeserializeMemory 4.51 μs 16.38 - 4056 B
NinoSerializeMemory 32.59 ns 1.00 4,004 B -
MemoryPackSerializeMemory 56.84 ns 1.74 4,004 B -
MessagePackSerializeMemory 3.05 μs 93.47 2,619 B -
NinoDeserializeReadOnlySequence 276.53 ns 1.00 - 4064 B
MemoryPackDeserializeReadOnlySequence 331.69 ns 1.20 - 4064 B
MessagePackDeserializeReadOnlySequence 4.54 μs 16.41 - 4064 B
NinoSerializeReadOnlySequence 34.83 ns 1.00 4,004 B -
MemoryPackSerializeReadOnlySequence 59.27 ns 1.70 4,004 B -
MessagePackSerializeReadOnlySequence 4.64 μs 133.21 2,619 B -
NinoDeserializeStringBuilder 151.98 ns 1.00 - 2072 B
MemoryPackDeserializeStringBuilder 163.36 ns 1.07 - 2072 B
MessagePackDeserializeStringBuilder 343.22 ns 2.26 - 4096 B
NinoSerializeStringBuilder 21.91 ns 1.00 2,003 B -
MemoryPackSerializeStringBuilder 33.89 ns 1.55 2,004 B -
MessagePackSerializeStringBuilder 234.43 ns 10.70 1,003 B 2024 B
NinoDeserializeUri 77.48 ns 1.00 - 184 B
MemoryPackDeserializeUri 85.50 ns 1.10 - 184 B
MessagePackDeserializeUri 106.27 ns 1.37 - 184 B
ProtobufDeserializeUri 375.22 ns 4.84 - 184 B
NinoSerializeUri 6.63 ns 1.00 53 B -
MemoryPackSerializeUri 21.87 ns 3.30 60 B -
MessagePackSerializeUri 34.93 ns 5.27 54 B -
ProtobufSerializeUri 85.69 ns 12.93 54 B -
NinoDeserializeVersion 7.10 ns 1.00 - 32 B
MemoryPackDeserializeVersion 17.63 ns 2.49 - 32 B
MessagePackDeserializeVersion 85.52 ns 12.08 - 104 B
NinoSerializeVersion 1.77 ns 1.00 16 B -
MemoryPackSerializeVersion 15.94 ns 8.98 17 B -
MessagePackSerializeVersion 56.03 ns 31.57 8 B 40 B
NinoDeserializeLookup 426.85 ns 1.00 - 5152 B
MemoryPackDeserializeLookup 602.12 ns 1.41 - 5576 B
MessagePackDeserializeLookup 4.74 μs 11.12 - 4127 B
NinoSerializeLookup 2.10 μs 1.00 4,084 B 320 B
MemoryPackSerializeLookup 7.21 μs 3.43 4,094 B 320 B
MessagePackSerializeLookup 3.73 μs 1.77 2,667 B 368 B
NinoDeserializeReadOnlyCollection 192.78 ns 1.00 - 4048 B
MemoryPackDeserializeReadOnlyCollection 236.15 ns 1.23 - 4048 B
MessagePackDeserializeReadOnlyCollection 4.03 μs 20.90 - 3383 B
NinoSerializeReadOnlyCollection 1.93 μs 1.00 4,004 B 40 B
MemoryPackSerializeReadOnlyCollection 6.80 μs 3.52 4,004 B 40 B
MessagePackSerializeReadOnlyCollection 4.27 μs 2.21 2,619 B 40 B

GenericPolymorphicTest

Method Mean Ratio Payload Allocated
NinoDeserializeGenericIntBase 11.45 μs 1.00 - 32024 B
MemoryPackDeserializeGenericIntBase 46.76 μs 4.08 - 32024 B
ProtobufDeserializeGenericIntBase 225.10 μs 19.66 - -
NinoSerializeGenericIntBase 3.36 μs 1.00 8,004 B -
MemoryPackSerializeGenericIntBase 54.89 μs 16.34 6,004 B -
ProtobufSerializeGenericIntBase 123.16 μs 36.67 5,967 B -
NinoDeserializeGenericManagedTupleBase 26.85 μs 1.00 - 72024 B
MemoryPackDeserializeGenericManagedTupleBase 70.57 μs 2.63 - -
ProtobufDeserializeGenericManagedTupleBase 329.18 μs 12.26 - 72037 B
NinoSerializeGenericManagedTupleBase 9.74 μs 1.00 14,004 B -
MemoryPackSerializeGenericManagedTupleBase 106.02 μs 10.89 19,004 B -
ProtobufSerializeGenericManagedTupleBase 209.97 μs 21.57 15,253 B -
NinoDeserializeGenericOpenClassTupleBase 23.34 μs 1.00 - 72024 B
MemoryPackDeserializeGenericOpenClassTupleBase 66.07 μs 2.83 - 72024 B
ProtobufDeserializeGenericOpenClassTupleBase 397.99 μs 17.05 - 72037 B
NinoSerializeGenericOpenClassTupleBase 8.60 μs 1.00 24,004 B -
MemoryPackSerializeGenericOpenClassTupleBase 107.41 μs 12.50 19,004 B -
ProtobufSerializeGenericOpenClassTupleBase 267.53 μs 31.13 13,681 B -
NinoDeserializeGenericSealedClassTupleBase 23.32 μs 1.00 - 72024 B
MemoryPackDeserializeGenericSealedClassTupleBase 64.91 μs 2.78 - 72024 B
ProtobufDeserializeGenericSealedClassTupleBase 402.68 μs 17.27 - 72037 B
NinoSerializeGenericSealedClassTupleBase 8.68 μs 1.00 21,004 B -
MemoryPackSerializeGenericSealedClassTupleBase 102.38 μs 11.79 19,004 B -
ProtobufSerializeGenericSealedClassTupleBase 271.98 μs 31.33 13,681 B -
NinoDeserializeGenericUnmanagedTupleBase 14.32 μs 1.00 - 40024 B
MemoryPackDeserializeGenericUnmanagedTupleBase 46.45 μs 3.24 - 40024 B
ProtobufDeserializeGenericUnmanagedTupleBase 277.94 μs 19.41 - 40032 B
NinoSerializeGenericUnmanagedTupleBase 5.48 μs 1.00 16,004 B -
MemoryPackSerializeGenericUnmanagedTupleBase 57.69 μs 10.55 18,004 B -
ProtobufSerializeGenericUnmanagedTupleBase 182.13 μs 33.29 10,253 B -
NinoDeserializeNonGenericBase 11.58 μs 1.00 - 32024 B
MemoryPackDeserializeNonGenericBase 25.35 μs 2.19 - 29297 B
MessagePackDeserializeNonGenericBase 70.17 μs 6.06 - 32024 B
ProtobufDeserializeNonGenericBase 215.82 μs 18.64 - 32030 B
NinoSerializeNonGenericBase 3.36 μs 1.00 8,004 B -
MemoryPackSerializeNonGenericBase 40.27 μs 12.00 6,004 B -
MessagePackSerializeNonGenericBase 50.14 μs 14.94 4,003 B -
ProtobufSerializeNonGenericBase 120.86 μs 36.02 5,967 B -

CommonTypesTest

Method Mean Ratio Payload Allocated
NinoDeserializeOrders 20.60 μs 1.00 - 88552 B
MemoryPackDeserializeOrders 27.37 μs 1.33 - 88552 B
MessagePackDeserializeOrders 100.62 μs 4.89 - 88552 B
ProtobufDeserializeOrders 102.44 μs 4.98 - 98986 B
NinoSerializeOrders 4.50 μs 1.00 23,726 B -
MemoryPackSerializeOrders 11.37 μs 2.53 27,990 B -
MessagePackSerializeOrders 46.47 μs 10.32 15,332 B -
ProtobufSerializeOrders 71.17 μs 15.80 15,843 B 4000 B
NinoDeserializePlayers 25.36 μs 1.00 - 120056 B
MemoryPackDeserializePlayers 36.60 μs 1.44 - 120056 B
MessagePackDeserializePlayers 97.47 μs 3.85 - 120056 B
ProtobufDeserializePlayers 212.30 μs 8.38 - 140088 B
NinoSerializePlayers 6.37 μs 1.00 36,164 B -
MemoryPackSerializePlayers 19.70 μs 3.09 43,364 B -
MessagePackSerializePlayers 50.72 μs 7.96 25,563 B -
ProtobufSerializePlayers 127.48 μs 20.00 33,579 B 4000 B
NinoDeserializeInventory 206.60 ns 1.00 - 8024 B
MemoryPackDeserializeInventory 215.88 ns 1.05 - 8024 B
MessagePackDeserializeInventory 12.37 μs 59.96 - 8024 B
ProtobufDeserializeInventory 28.41 μs 137.71 - 8024 B
NinoSerializeInventory 40.58 ns 1.00 8,004 B -
MemoryPackSerializeInventory 43.50 ns 1.07 8,004 B -
MessagePackSerializeInventory 4.80 μs 118.29 5,619 B -
ProtobufSerializeInventory 26.29 μs 648.08 7,518 B -
NinoDeserializeShapes 6.29 μs 1.00 - 36056 B
MemoryPackDeserializeShapes 8.70 μs 1.38 - 36056 B
MessagePackDeserializeShapes 32.15 μs 5.11 - 36056 B
ProtobufDeserializeShapes 103.45 μs 16.45 - 36089 B
NinoSerializeShapes 1.03 μs 1.00 14,004 B -
MemoryPackSerializeShapes 12.17 μs 11.81 12,004 B -
MessagePackSerializeShapes 19.78 μs 19.20 13,119 B -
ProtobufSerializeShapes 69.82 μs 67.79 15,365 B -
NinoDeserializeStringDictionary 20.37 μs 1.01 - 110216 B
MemoryPackDeserializeStringDictionary 32.46 μs 1.61 - 110216 B
MessagePackDeserializeStringDictionary 40.44 μs 2.00 - 110216 B
ProtobufDeserializeStringDictionary 77.11 μs 3.82 - 181416 B
NinoSerializeStringDictionary 4.97 μs 1.00 16,734 B -
MemoryPackSerializeStringDictionary 20.58 μs 4.14 30,734 B -
MessagePackSerializeStringDictionary 16.59 μs 3.34 16,733 B -
ProtobufSerializeStringDictionary 35.94 μs 7.23 20,730 B 40 B
NinoDeserializeTree 29.95 μs 1.00 - 171872 B
MemoryPackDeserializeTree 36.54 μs 1.22 - 171872 B
MessagePackDeserializeTree 83.87 μs 2.80 - 171872 B
NinoSerializeTree 6.95 μs 1.00 27,163 B -
MemoryPackSerializeTree 11.98 μs 1.73 32,623 B -
MessagePackSerializeTree 45.30 μs 6.52 20,338 B -
NinoDeserializeWaypoints 547.47 ns 1.00 - 24024 B
MemoryPackDeserializeWaypoints 640.25 ns 1.17 - 24024 B
MessagePackDeserializeWaypoints 39.46 μs 72.16 - -
ProtobufDeserializeWaypoints 64.28 μs 117.53 - 24024 B
NinoSerializeWaypoints 165.75 ns 1.00 15,004 B -
MemoryPackSerializeWaypoints 147.06 ns 0.89 24,004 B -
MessagePackSerializeWaypoints 21.94 μs 132.61 16,979 B -
ProtobufSerializeWaypoints 63.09 μs 381.42 19,482 B -

PolymorphicTest

Method Mean Ratio Payload Allocated
NinoDeserializePolymorphicClassArray 164.54 μs 1.00 - 745624 B
MemoryPackDeserializePolymorphicClassArray 302.23 μs 1.84 - 745624 B
MessagePackDeserializePolymorphicClassArray 1.47 ms 8.95 - 1162424 B
ProtobufDeserializePolymorphicClassArray 5.25 ms 31.92 - 1211492 B
NinoSerializePolymorphicClassArray 39.09 μs 1.00 397,304 B -
MemoryPackSerializePolymorphicClassArray 251.18 μs 6.43 384,404 B -
MessagePackSerializePolymorphicClassArray 749.89 μs 19.19 487,103 B -
ProtobufSerializePolymorphicClassArray 3.76 ms 96.22 635,807 B 8000 B
NinoDeserializePolymorphicClass 14.17 μs 1.00 - 79688 B
MemoryPackDeserializePolymorphicClass 28.20 μs 1.99 - 79688 B
MessagePackDeserializePolymorphicClass 195.61 μs 13.83 - -
ProtobufDeserializePolymorphicClass 780.20 μs 55.18 - 84377 B
NinoSerializePolymorphicClass 4.61 μs 1.00 53,789 B -
MemoryPackSerializePolymorphicClass 24.17 μs 5.24 51,760 B -
MessagePackSerializePolymorphicClass 112.65 μs 24.43 66,859 B -
ProtobufSerializePolymorphicClass 640.79 μs 138.98 91,495 B 80 B
NinoDeserializePolymorphicFooArray 13.42 μs 1.00 - 76024 B
MemoryPackDeserializePolymorphicFooArray 29.68 μs 2.21 - 76024 B
MessagePackDeserializePolymorphicFooArray 189.84 μs 14.16 - -
ProtobufDeserializePolymorphicFooArray 771.16 μs 57.53 - 76052 B
NinoSerializePolymorphicFooArray 4.83 μs 1.00 52,404 B -
MemoryPackSerializePolymorphicFooArray 30.30 μs 6.27 50,404 B -
MessagePackSerializePolymorphicFooArray 101.02 μs 20.91 65,203 B -
ProtobufSerializePolymorphicFooArray 648.85 μs 134.28 89,500 B -

VersionTolerantTest

Method Mean Ratio Payload Allocated
NinoDeserializeTolerantClass 370.89 ns 1.00 - 2288 B
MemoryPackDeserializeTolerantClass 823.27 ns 2.22 - 2528 B
MessagePackDeserializeTolerantClass 1.86 μs 5.01 - -
ProtobufDeserializeTolerantClass 6.20 μs 16.74 - -
NinoSerializeTolerantClass 101.14 ns 1.00 1,073 B -
MemoryPackSerializeTolerantClass 515.47 ns 5.10 1,164 B -
MessagePackSerializeTolerantClass 1.12 μs 11.11 1,068 B -
ProtobufSerializeTolerantClass 3.56 μs 35.19 1,253 B 80 B
NinoDeserializeTolerantClasses 42.22 μs 1.00 - 229624 B
MemoryPackDeserializeTolerantClasses 84.57 μs 2.01 - 253624 B
MessagePackDeserializeTolerantClasses 197.67 μs 4.69 - 253624 B
ProtobufDeserializeTolerantClasses 595.37 μs 14.12 - -
NinoSerializeTolerantClasses 10.78 μs 1.00 107,304 B -
MemoryPackSerializeTolerantClasses 50.40 μs 4.68 116,404 B -
MessagePackSerializeTolerantClasses 114.03 μs 10.58 106,801 B -
ProtobufSerializeTolerantClasses 351.91 μs 32.65 124,968 B 8000 B

CollectionsTest

Method Mean Ratio Payload Allocated
NinoDeserializeHashSet 2.09 μs 1.00 - 17768 B
MemoryPackDeserializeHashSet 6.36 μs 3.05 - 17768 B
MessagePackDeserializeHashSet 12.63 μs 6.05 - 58664 B
ProtobufDeserializeHashSet 25.26 μs 12.09 - 58738 B
NinoSerializeHashSet 120.47 ns 1.00 4,004 B -
MemoryPackSerializeHashSet 6.39 μs 53.02 4,004 B -
MessagePackSerializeHashSet 4.98 μs 41.34 2,874 B -
ProtobufSerializeHashSet 12.39 μs 102.87 2,957 B 40 B
NinoDeserializeIntDictionary 2.39 μs 1.00 - 22192 B
MemoryPackDeserializeIntDictionary 6.46 μs 2.70 - 22192 B
MessagePackDeserializeIntDictionary 11.47 μs 4.79 - 22192 B
ProtobufDeserializeIntDictionary 59.34 μs 24.78 - -
NinoSerializeIntDictionary 133.41 ns 1.00 8,004 B -
MemoryPackSerializeIntDictionary 2.52 μs 18.92 8,004 B -
MessagePackSerializeIntDictionary 6.72 μs 50.36 5,818 B -
ProtobufSerializeIntDictionary 32.30 μs 242.12 7,934 B 40 B
NinoDeserializeKeyValuePairs 305.32 ns 1.00 - 8024 B
MemoryPackDeserializeKeyValuePairs 335.98 ns 1.10 - 8024 B
MessagePackDeserializeKeyValuePairs 24.09 μs 78.89 - -
ProtobufDeserializeKeyValuePairs 44.73 μs 146.51 - 8024 B
NinoSerializeKeyValuePairs 87.68 ns 1.00 8,004 B -
MemoryPackSerializeKeyValuePairs 88.41 ns 1.01 8,004 B -
MessagePackSerializeKeyValuePairs 16.47 μs 187.87 6,563 B -
ProtobufSerializeKeyValuePairs 35.28 μs 402.33 7,853 B -
NinoDeserializeQueue 195.73 ns 1.00 - 4064 B
MemoryPackDeserializeQueue 3.05 μs 15.58 - -
MessagePackDeserializeQueue 5.31 μs 27.11 - 4064 B
ProtobufDeserializeQueue 17.00 μs 86.85 - -
NinoSerializeQueue 46.80 ns 1.00 4,004 B -
MemoryPackSerializeQueue 6.21 μs 132.75 4,004 B -
MessagePackSerializeQueue 4.50 μs 96.10 2,619 B -
ProtobufSerializeQueue 10.31 μs 220.34 2,872 B -
NinoDeserializeTupleList 780.57 ns 1.00 - 16056 B
MemoryPackDeserializeTupleList 861.26 ns 1.10 - 16056 B
MessagePackDeserializeTupleList 25.16 μs 32.23 - 16057 B
ProtobufDeserializeTupleList 39.30 μs 50.35 - 16089 B
NinoSerializeTupleList 169.70 ns 1.00 12,004 B -
MemoryPackSerializeTupleList 221.03 ns 1.30 16,004 B -
MessagePackSerializeTupleList 16.89 μs 99.69 6,605 B -
ProtobufSerializeTupleList 37.89 μs 223.61 8,338 B -
NinoDeserializeConcurrentDictionary 13.76 μs 1.00 - 49112 B
MemoryPackDeserializeConcurrentDictionary 59.60 μs 4.33 - 141032 B
MessagePackDeserializeConcurrentDictionary 67.33 μs 4.89 - 141032 B
ProtobufDeserializeConcurrentDictionary 116.61 μs 8.48 - 141035 B
NinoSerializeConcurrentDictionary 2.38 μs 1.00 8,004 B -
MemoryPackSerializeConcurrentDictionary 3.88 μs 1.63 8,004 B -
MessagePackSerializeConcurrentDictionary 10.57 μs 4.43 5,563 B 56 B
ProtobufSerializeConcurrentDictionary 47.41 μs 19.90 7,849 B 96 B
NinoDeserializeLinkedList 11.09 μs 1.00 - 48040 B
MemoryPackDeserializeLinkedList 14.37 μs 1.30 - 48040 B
MessagePackDeserializeLinkedList 18.40 μs 1.66 - 48040 B
ProtobufDeserializeLinkedList 32.01 μs 2.89 - 48041 B
NinoSerializeLinkedList 1.56 μs 1.00 4,004 B -
MemoryPackSerializeLinkedList 7.91 μs 5.06 4,004 B -
MessagePackSerializeLinkedList 3.66 μs 2.34 2,619 B -
ProtobufSerializeLinkedList 17.89 μs 11.45 2,872 B 48 B
NinoDeserializeSelfTree 99.24 μs 1.00 - 251192 B
MemoryPackDeserializeSelfTree 156.10 μs 1.57 - 251196 B
NinoSerializeSelfTree 27.32 μs 1.00 21,844 B -
MemoryPackSerializeSelfTree 117.23 μs 4.29 21,844 B 54600 B
NinoDeserializeSortedDictionary 17.94 μs 1.00 - 48112 B
MemoryPackDeserializeSortedDictionary 52.72 μs 2.95 - -
MessagePackDeserializeSortedDictionary 63.01 μs 3.53 - 48112 B
ProtobufDeserializeSortedDictionary 132.40 μs 7.41 - 48113 B
NinoSerializeSortedDictionary 3.24 μs 1.00 8,004 B -
MemoryPackSerializeSortedDictionary 6.69 μs 2.06 8,004 B 200 B
MessagePackSerializeSortedDictionary 16.84 μs 5.19 5,563 B 200 B
ProtobufSerializeSortedDictionary 54.43 μs 16.78 7,849 B 288 B
NinoDeserializeSortedList 1.91 μs 1.00 - 8112 B
MemoryPackDeserializeSortedList 16.31 μs 8.56 - 8112 B
MessagePackDeserializeSortedList 22.83 μs 11.97 - 8112 B
ProtobufDeserializeSortedList 73.72 μs 38.66 - 16848 B
NinoSerializeSortedList 1.88 μs 1.00 8,004 B -
MemoryPackSerializeSortedList 3.40 μs 1.81 8,004 B -
MessagePackSerializeSortedList 9.21 μs 4.90 5,563 B 48 B
ProtobufSerializeSortedList 47.51 μs 25.29 7,849 B 88 B
NinoDeserializeSortedSet 15.83 μs 1.00 - 40048 B
MemoryPackDeserializeSortedSet 53.92 μs 3.41 - 40048 B
MessagePackDeserializeSortedSet 51.55 μs 3.26 - -
ProtobufDeserializeSortedSet 63.22 μs 3.99 - -
NinoSerializeSortedSet 2.84 μs 1.00 4,004 B -
MemoryPackSerializeSortedSet 11.15 μs 3.93 4,004 B 200 B
MessagePackSerializeSortedSet 11.99 μs 4.23 2,947 B 248 B
ProtobufSerializeSortedSet 22.21 μs 7.83 2,981 B 248 B

The cases ran in parallel on several runners (every serializer's run of a case on one):

  • PooledTest, ReferenceTest, SimpleTest: AMD EPYC 7763 2.72GHz, 1 CPU, 4 logical and 2 physical cores
  • FixedSizeTest, PooledGraphTest, ScatteredTest, UntrustedTest: AMD EPYC 7763 3.23GHz, 1 CPU, 4 logical and 2 physical cores
  • BclTypesTest, GenericPolymorphicTest: AMD EPYC 7763 2.61GHz, 1 CPU, 4 logical and 2 physical cores
  • CommonTypesTest, PolymorphicTest: AMD EPYC 9V74 3.60GHz, 1 CPU, 4 logical and 2 physical cores
  • BclTypesTest, CommonTypesTest: AMD EPYC 9V45 4.21GHz, 1 CPU, 4 logical and 2 physical cores
  • BclTypesTest (NinoDeserializeBlockingCollection to ProtobufSerializeImmutableStack): AMD EPYC 7763 3.24GHz, 1 CPU, 4 logical and 2 physical cores
  • BclTypesTest (NinoDeserializeImmutableSortedDictionary to MessagePackSerializeVersion): INTEL XEON PLATINUM 8573C 3.09GHz, 1 CPU, 4 logical and 2 physical cores
  • SimpleTest, VersionTolerantTest: AMD EPYC 7763 2.59GHz, 1 CPU, 4 logical and 2 physical cores
  • BclTypesTest, CollectionsTest: AMD EPYC 9V74 3.69GHz, 1 CPU, 4 logical and 2 physical cores
  • CollectionsTest (NinoDeserializeConcurrentDictionary to ProtobufSerializeSortedSet): AMD EPYC 7763 3.22GHz, 1 CPU, 4 logical and 2 physical cores

Median, Min, Max and the GC counts: benchmark-x64-full.md.


Benchmark generated automatically on 2026-11-04 16:09:33 UTC

🌐 TypeScript runtime benchmark

Generated TypeScript of v4.0.0-rc.8 against Protocol Buffers, MessagePack and JSON, in Node

The code nino-gen generates for runtimes/typescript/bench (Program.cs's types), against Protocol Buffers (protobuf-es), MessagePack (msgpackr) and JSON (to and from UTF-8 bytes), in Node v27.0.0-alpha.0.0.0, on data .NET wrote, timed by tinybench 6.2.2: the mean time of a pass over each scenario's values (each pass the next of 64 variants, so that no pass reads what the one before read), ± its relative margin of error, and passes per second.

Scenario Operation Nino Protobuf MessagePack JSON
Player x1000 serialize 273.33 μs ±0.5% (3,659/s) 2.96 ms ±6% (337/s) 2.11 ms ±0.7% (473/s) 1.77 ms ±5% (565/s)
Player x1000 serializeInto 172.38 μs ±0.3% (5,801/s)
Player x1000 deserialize 719.03 μs ±3% (1,391/s) 3.24 ms ±8% (309/s) 1.68 ms ±0.4% (597/s) 1.84 ms ±0.3% (544/s)
Player x1000 size 118.3 KiB 100.2 KiB 187.9 KiB 285.5 KiB
Zoo 50 x 100 animals (type ids) serialize 195.63 μs ±0.5% (5,112/s) 1.32 ms ±2% (757/s) 1.88 ms ±0.7% (532/s) 473.60 μs ±0.7% (2,112/s)
Zoo 50 x 100 animals (type ids) serializeInto 180.87 μs ±0.4% (5,529/s)
Zoo 50 x 100 animals (type ids) deserialize 154.10 μs ±0.4% (6,489/s) 3.06 ms ±6% (326/s) 1.14 ms ±2% (877/s) 1.76 ms ±1.0% (568/s)
Zoo 50 x 100 animals (type ids) size 71.4 KiB 71.3 KiB 117.5 KiB 161.2 KiB
OrderBook 100 x 50 orders serialize 450.29 μs ±0.3% (2,221/s) 2.64 ms ±4% (379/s) 3.38 ms ±0.8% (296/s) 1.20 ms ±0.7% (832/s)
OrderBook 100 x 50 orders serializeInto 416.93 μs ±0.2% (2,398/s)
OrderBook 100 x 50 orders deserialize 446.26 μs ±0.7% (2,241/s) 4.50 ms ±5% (222/s) 3.20 ms ±1% (313/s) 1.54 ms ±0.5% (649/s)
OrderBook 100 x 50 orders size 225.1 KiB 235.9 KiB 433.9 KiB 551.3 KiB
Article x200 (text) serialize 217.25 μs ±0.6% (4,603/s) 1.85 ms ±4% (541/s) 422.82 μs ±0.5% (2,365/s) 895.56 μs ±3% (1,117/s)
Article x200 (text) serializeInto 143.70 μs ±0.2% (6,959/s)
Article x200 (text) deserialize 189.22 μs ±0.7% (5,285/s) 791.97 μs ±7% (1,263/s) 468.33 μs ±0.4% (2,135/s) 789.73 μs ±0.6% (1,266/s)
Article x200 (text) size 430.2 KiB 421.6 KiB 428.2 KiB 434.2 KiB
Mesh 20 x (2 x 500 Vec3, 1500 int) serialize 245.79 μs ±0.6% (4,069/s) 3.87 ms ±1% (258/s) 7.47 ms ±0.5% (134/s) 4.26 ms ±0.5% (235/s)
Mesh 20 x (2 x 500 Vec3, 1500 int) serializeInto 201.08 μs ±0.3% (4,973/s)
Mesh 20 x (2 x 500 Vec3, 1500 int) deserialize 337.36 μs ±6% (2,964/s) 7.47 ms ±7% (134/s) 3.23 ms ±1% (310/s) 5.22 ms ±0.9% (191/s)
Mesh 20 x (2 x 500 Vec3, 1500 int) size 351.9 KiB 383.3 KiB 769.5 KiB 1514.3 KiB
Samples 20 x (3 x 1000 numbers) serialize 128.06 μs ±0.9% (7,809/s) 1.20 ms ±0.9% (831/s) 944.94 μs ±0.5% (1,058/s) 2.07 ms ±0.6% (483/s)
Samples 20 x (3 x 1000 numbers) serializeInto 96.55 μs ±0.1% (10,357/s)
Samples 20 x (3 x 1000 numbers) deserialize 184.79 μs ±1% (5,411/s) 765.46 μs ±0.8% (1,306/s) 884.34 μs ±0.8% (1,131/s) 1.93 ms ±0.6% (518/s)
Samples 20 x (3 x 1000 numbers) size 312.8 KiB 293.0 KiB 448.0 KiB 876.1 KiB
Lookup 50 x (2 maps of 100) serialize 356.19 μs ±0.3% (2,807/s) 1.99 ms ±2% (503/s) 690.45 μs ±0.4% (1,448/s) 1.24 ms ±2% (810/s)
Lookup 50 x (2 maps of 100) serializeInto 331.63 μs ±0.1% (3,015/s)
Lookup 50 x (2 maps of 100) deserialize 1.04 ms ±1% (962/s) 4.20 ms ±4% (238/s) 2.06 ms ±2% (484/s) 6.14 ms ±12% (163/s)
Lookup 50 x (2 maps of 100) size 156.2 KiB 178.4 KiB 147.7 KiB 194.5 KiB
Event x1000 (long) serialize 80.24 μs ±0.6% (12,463/s) 1.35 ms ±4% (740/s) 911.73 μs ±0.3% (1,097/s) 1.73 ms ±5% (579/s)
Event x1000 (long) serializeInto 41.71 μs ±0.4% (23,976/s)
Event x1000 (long) deserialize 66.87 μs ±0.8% (14,955/s) 937.68 μs ±10% (1,066/s) 273.14 μs ±0.5% (3,661/s) 800.90 μs ±0.6% (1,249/s)
Event x1000 (long) size 55.7 KiB 50.6 KiB 77.1 KiB 151.9 KiB
Strings 1000 (ASCII) serialize 27.07 μs ±0.7% (36,945/s) 50.97 μs ±0.4% (19,621/s) 44.13 μs ±0.2% (22,659/s) 13.05 μs ±0.4% (76,599/s)
Strings 1000 (ASCII) serializeInto 25.89 μs ±0.1% (38,620/s)
Strings 1000 (ASCII) deserialize 17.93 μs ±0.5% (55,781/s) 109.87 μs ±0.5% (9,102/s) 26.96 μs ±0.5% (37,098/s) 30.47 μs ±0.4% (32,818/s)
Strings 1000 (ASCII) size 10.6 KiB 11.6 KiB 10.6 KiB 12.6 KiB
Strings 1000 (ASCII, Latin-1, other) serialize 38.97 μs ±0.3% (25,661/s) 133.09 μs ±0.3% (7,514/s) 65.94 μs ±0.2% (15,165/s) 32.71 μs ±0.3% (30,567/s)
Strings 1000 (ASCII, Latin-1, other) serializeInto 36.89 μs ±0.1% (27,107/s)
Strings 1000 (ASCII, Latin-1, other) deserialize 34.80 μs ±0.3% (28,738/s) 298.82 μs ±0.3% (3,347/s) 169.86 μs ±0.2% (5,887/s) 50.47 μs ±0.3% (19,814/s)
Strings 1000 (ASCII, Latin-1, other) size 20.4 KiB 21.4 KiB 20.4 KiB 22.4 KiB
IntSet 20 x 1000 serialize 86.11 μs ±0.4% (11,612/s) 594.32 μs ±1% (1,683/s) 331.15 μs ±0.3% (3,020/s) 554.09 μs ±0.4% (1,805/s)
IntSet 20 x 1000 serializeInto 74.84 μs ±0.1% (13,362/s)
IntSet 20 x 1000 deserialize 696.00 μs ±0.3% (1,437/s) 985.55 μs ±0.4% (1,015/s) 841.54 μs ±0.6% (1,188/s) 1.17 ms ±0.3% (852/s)
IntSet 20 x 1000 size 78.2 KiB 95.3 KiB 97.9 KiB 205.0 KiB
StringSet 20 x 1000 serialize 514.01 μs ±0.4% (1,945/s) 1.04 ms ±0.9% (962/s) 1.00 ms ±0.3% (998/s) 301.14 μs ±0.9% (3,321/s)
StringSet 20 x 1000 serializeInto 489.57 μs ±0.2% (2,043/s)
StringSet 20 x 1000 deserialize 4.55 ms ±3% (220/s) 7.33 ms ±2% (136/s) 4.84 ms ±3% (207/s) 5.05 ms ±3% (198/s)
StringSet 20 x 1000 size 212.8 KiB 232.2 KiB 212.9 KiB 252.0 KiB

🐹 Go runtime benchmark

The Go runtime of v4.0.0-rc.8 against Protocol Buffers, MessagePack, encoding/json and gob

runtimes/go/bench (testing.B, summarized by benchstat: medians ± their 95% confidence interval): codecs in the shape nino-gen generates (Nino) and the struct tags (NinoTags) against Protocol Buffers (google.golang.org/protobuf), MessagePack (vmihailenco/msgpack), encoding/json and gob, on Intel(R) Xeon(R) Platinum 8370C CPU @ 2.80GHz, linux/amd64. Allocations are counted per operation; sizes are the encoded bytes.

Case Operation Nino NinoTags Protobuf MessagePack JSON Gob
simple-class serialize 467.10 ns ±3% 974.95 ns ±1% 4.74 μs ±1% 6.14 μs ±2% 9.16 μs ±1% 8.63 μs ±2%
simple-class deserialize 1.37 μs ±6% 1.88 μs ±1% 6.76 μs ±1% 11.56 μs ±1% 16.41 μs ±3% 24.55 μs ±3%
simple-class serialize allocs 0 1 80 41 15 65
simple-class deserialize allocs 12 16 66 64 29 254
simple-class size 1.0 KiB 1.0 KiB 1.1 KiB 1.2 KiB 1.9 KiB 1.3 KiB
simple-class-100 serialize 47.02 μs ±1% 95.95 μs ±1% 485.01 μs ±3% 616.08 μs ±0% 953.15 μs ±2% 439.60 μs ±1%
simple-class-100 deserialize 137.32 μs ±1% 171.67 μs ±4% 688.44 μs ±1% 1.15 ms ±2% 1.69 ms ±2% 546.82 μs ±3%
simple-class-100 serialize allocs 0 101 8000 4101 1403 4042
simple-class-100 deserialize allocs 1204 1407 6609 6208 2839 2749
simple-class-100 size 104.4 KiB 104.4 KiB 111.4 KiB 122.9 KiB 193.1 KiB 107.1 KiB
scattered-1m serialize 140.05 ms ±5% 277.03 ms ±4% 383.70 ms ±6% 512.53 ms ±7% 757.49 ms ±7% 454.93 ms ±5%
scattered-1m deserialize 84.49 ms ±8% 157.92 ms ±7% 257.09 ms ±7% 506.83 ms ±4% 822.84 ms ±1% 269.23 ms ±10%
scattered-1m serialize allocs 0 1 0 1 32 64
scattered-1m deserialize allocs 1017237 1017241 3000039 3000011 3000044 3000187
scattered-1m size 48611.1 KiB 48611.1 KiB 52501.2 KiB 70095.5 KiB 92173.2 KiB 52879.2 KiB
int-set-1000 serialize 69.29 ns ±3% 146.25 ns ±0% 7.53 μs ±1% 29.66 μs ±2% 29.53 μs ±3% 15.64 μs ±3%
int-set-1000 deserialize 3.82 μs ±3% 1.42 μs ±2% 9.83 μs ±2% 44.57 μs ±1% 69.70 μs ±1% 22.05 μs ±2%
int-set-1000 serialize allocs 0 1 0 1 3 24
int-set-1000 deserialize allocs 2 5 2 8 13 154
int-set-1000 size 3.9 KiB 3.9 KiB 4.8 KiB 4.9 KiB 10.2 KiB 4.9 KiB
string-set-1000 serialize 2.39 μs ±4% 12.44 μs ±0% 14.85 μs ±2% 35.32 μs ±1% 46.18 μs ±1% 34.42 μs ±1%
string-set-1000 deserialize 33.38 μs ±2% 56.98 μs ±2% 43.91 μs ±4% 129.49 μs ±3% 151.28 μs ±2% 95.36 μs ±2%
string-set-1000 serialize allocs 0 1 0 1 3 27
string-set-1000 deserialize allocs 38 1010 1012 2008 2013 2154
string-set-1000 size 10.6 KiB 10.6 KiB 11.6 KiB 10.6 KiB 12.6 KiB 10.7 KiB
strings-1000 serialize 2.31 μs ±3% 2.47 μs ±5% 13.70 μs ±2% 11.28 μs ±2% 32.89 μs ±1% 22.35 μs ±3%
strings-1000 deserialize 12.58 μs ±2% 12.84 μs ±3% 41.61 μs ±1% 43.46 μs ±2% 90.48 μs ±3% 46.06 μs ±3%
strings-1000 serialize allocs 0 1 0 1 3 27
strings-1000 deserialize allocs 7 9 1012 1004 1013 1153
strings-1000 size 10.6 KiB 10.6 KiB 11.6 KiB 10.6 KiB 12.6 KiB 10.7 KiB
strings-mixed-1000 serialize 8.41 μs ±0% 8.51 μs ±1% 28.14 μs ±1% 11.23 μs ±1% 60.63 μs ±0% 34.73 μs ±2%
strings-mixed-1000 deserialize 27.30 μs ±3% 27.60 μs ±3% 60.20 μs ±3% 46.29 μs ±5% 157.48 μs ±3% 47.15 μs ±1%
strings-mixed-1000 serialize allocs 0 1 0 1 3 29
strings-mixed-1000 deserialize allocs 12 14 1012 1004 1013 1153
strings-mixed-1000 size 20.4 KiB 20.4 KiB 21.4 KiB 20.4 KiB 22.3 KiB 20.4 KiB
strings-mixed-utf8-1000 serialize 14.48 μs ±1%
strings-mixed-utf8-1000 deserialize 28.59 μs ±11%
strings-mixed-utf8-1000 serialize allocs 0
strings-mixed-utf8-1000 deserialize allocs 12
strings-mixed-utf8-1000 size 21.4 KiB