Repository navigation
v4.0.0-rc.7
Pre-releaseInstall: dotnet add package Nino --version 4.0.0-rc.7 (Unity package: 4.0.0-preview.207)
New
- The Unity package includes Nino's code fixes: Rider and Visual Studio offer the same quick fixes for Nino's analyzer warnings as in a NuGet project.
Performance
- Serializing a list or array of fixed-size values to a
byte[]writes straight into an array of its size, without a copy out of the scratch buffer. - Without AVX-512, lists and arrays of value tuples and other padded raw values are moved with in-lane shuffles (AVX2, SSSE3) instead of cross-lane permutes or one value at a time: a list of 1,000
(int, long)serializes in 194 ns instead of 380 ns (GitHub's x64 runners, EPYC 7763, AVX2). - On Intel x64 CPUs, deserializing into a new list or array of padded value tuples or
DateTimeOffsets is faster: the array is allocated zeroed, as stores into an uninitialized array read each cache line from memory first. - Copies of 33 to 128 bytes are inline vector moves instead of a
memmovecall, and reads of raw values into an existing array use Nino's vector copy: small raw arrays, lists and non-Latin-1 strings read faster. - Multi-dimensional arrays of fixed-size elements (tuples,
DateTimeOffsets, raw structs) are read and written as blocks instead of one value at a time. ConcurrentStack<T>andConcurrentBag<T>of fixed-size elements serialize as one block, andPriorityQueuereads and writes andHashSetwrites of non-raw elements loop on a local reader or writer.- Nullable members of raw types (
int?,long?,double?,DateTime?...) are read under one bounds check for the flag and the value. Versionand the Unity types of Nino.UnityTypes (AnimationCurve,Gradient,RectOffset) reserve their bytes at once and read each run of keys under one bounds check.- Serializing an
ImmutableStack<T>(Nino.Immutable) allocates nothing, as it no longer builds and reverses a list: 1,000 ints in 2.7 µs instead of 4.5 µs on GitHub's Arm64 runners (Neoverse-N2) and 2.9 µs instead of 3.1 µs on x64 (EPYC 7763). - Serializing collections without a specialized path (
Collection<T>,ObservableCollection<T>, customICollection<T>types) no longer stores and reloads the writer around every element. - Version-tolerant types read adjacent raw members as one block whenever the data holds all of them: the benchmark's array of version-tolerant classes deserializes in 49.8 µs instead of 57.3 µs (GitHub's x64 runners, EPYC 7763).
- Serializing keyed types is a little faster: a keyed member's key and region length, and the raw members just before it, are reserved at once.
- Lists of
[NinoPooled]types deserialize a little faster: renting from the pool is inlined into their element loops. - Serializing arrays and lists of tuples,
KeyValuePairs and other padded structs takes the same time wherever the output buffer starts, instead of slowing down at some alignments. - New arrays of 2 to 6 KB that a read fills are allocated zeroed, which is cheaper there than uninitialized: 1,000 ints deserialize into a
ReadOnlyCollection<T>orReadOnlySequence<T>in 257 and 264 ns instead of 348 ns (GitHub's x64 runners, EPYC 7763) and in 364 and 363 ns instead of 416 and 423 ns (Arm64 runners, Neoverse-N2), and into aMemory<T>in 262 ns instead of 312 ns (x64). - On Arm64, arrays and lists of padded structs (tuples,
KeyValuePairs) are copied with load and store pairs, 32 bytes a step. - Block copies of 8 to 32 KB (4 to 16 KB without 64-byte vectors) use Nino's vector loop instead of
memmove, which is several times slower on some AMD EPYC CPUs. - Arrays and lists of 5-byte values such as
(byte, int)move several values at a time with AVX2 and SSSE3 (x64 without AVX-512), instead of one at a time. HashSet<T>andDictionary<TKey, TValue>of raw values are written from their entries in one loop when no entry has been removed, on .NET, .NET Framework, Mono and Unity.- On Arm64, arrays and lists of small padded tuples read and write faster: 1,000
(byte, int)deserialize in 805 ns instead of 1,123 ns and serialize in 122 ns instead of 422 ns, 1,000(short, int)in 801 ns instead of 1,103 ns and 124 ns instead of 200 ns, and 1,000(int, long)in 1,415 ns instead of 1,815 ns and 219 ns instead of 375 ns (GitHub's Arm64 runners). - Subtypes read or written through a generic base (
abstract class Box<T>,IRepository<T>) take the same generated dispatch as through a non-generic base, for the subtypes of each closed base declared in its assembly, instead of a registry lookup and a virtual call for each value. - Subtypes of a generic base whose type argument is a reference type or a tuple holding one (
Box<string>,Box<(int, string)>) read and write the base's members without a formatter lookup and a virtual call for each value. - On x64 CPUs without AVX-512, block copies of up to 1 MB use Nino's 32-byte vector loop instead of
memmove. - Nino.Immutable reads
ImmutableDictionaryandImmutableHashSetby building their tree directly in one pass: 1,000 ints deserialize in 20.6 and 19.7 µs instead of 109.4 and 92.1 µs (GitHub's x64 runners, EPYC 7763) and in 21.3 and 20.4 µs instead of 81.7 and 78.8 µs (Arm64 runners, Neoverse-N2). - Nino.Immutable reads
ImmutableSortedDictionaryandImmutableSortedSetby building their tree directly in one pass: 1,000 ints deserialize in 13.3 and 13.2 µs instead of 55.6 and 18.5 µs (GitHub's x64 runners, EPYC 7763) and in 14.6 and 13.8 µs instead of 62.5 and 19.1 µs (Arm64 runners, Neoverse-N2). LinkedList<T>andSortedDictionary<TKey, TValue>are read by linking their nodes or building their tree directly: 1,000 ints deserialize into aLinkedListin 11.5 µs instead of 14.1 µs (GitHub's x64 runners, EPYC 7763) and 12.3 µs instead of 14.5 µs (Arm64 runners, Neoverse-N2), and into aSortedDictionaryin 17.4 µs instead of 23.9 µs and 16.7 µs instead of 27.1 µs.- Nino.Immutable writes
ImmutableDictionary,ImmutableHashSet,ImmutableSortedDictionaryandImmutableSortedSetstraight out of their trees: 1,000 ints serialize 4 to 5 times as fast for the first two and 2 to 2.6 times as fast for the sorted ones (GitHub's x64 runners, EPYC 7763, and Arm64 runners, Neoverse-N2). SortedSet<T>andSortedDictionary<TKey, TValue>serialize copied out of their trees: 1,000 ints in 2.8 µs instead of 5.5 µs (SortedSet) and 3.0 to 3.2 µs instead of 5.6 to 5.8 µs (SortedDictionary) (GitHub's x64 runners, EPYC 7763, and Arm64 runners, Neoverse-N2).- Empty collections skip their element loops, and a pool that was never filled answers a rent without a call or a thread-local access: a pooled node deserializes in 156 ns instead of 205 ns (GitHub's x64 runners, EPYC 7763) and 145 ns instead of 193 ns (Arm64 runners, Neoverse-N2).
- New
HashSet<T>andDictionary<TKey, TValue>of raw elements are filled directly: 1,000 ints deserialize into aHashSetin 2.6 µs instead of 6.1 µs (GitHub's x64 runners, EPYC 7763) and 2.8 µs instead of 5.3 µs (Arm64 runners, Neoverse-N2). - On Arm64, block copies of up to 64 KB use paired vector loads from an aligned source instead of
memmove. - On AMD x64 CPUs, reads into a new array copy with vector stores instead of
memmove, whoserep movsbis slower there: 1,000KeyValuePair<int, int>deserialize in 418 ns instead of 465 ns (GitHub's x64 runners, EPYC 7763). - Arrays of polymorphic classes are faster both ways: 100 of them serialize in 34 µs instead of 46 µs and deserialize in 172 µs instead of 224 µs (GitHub's x64 runners, Xeon 6973P-C), and serialize in 58 µs instead of 76 µs and deserialize in 193 µs instead of 221 µs (Arm64 runners, Neoverse-N2).
Fixes
- Serializing a span of unmanaged values past 4 GB, a
StringBuilderpast 1 G chars or UTF-8 text past 2 GB throwsOverflowExceptioninstead of writing past the end of the buffer. - Generic abstract classes, generic interfaces and abstract classes nested in generic types with subtypes no longer break the build: their subtypes are written and read through the registry, in every format.
- A class nested in a generic class with [NinoType] subtypes in another assembly no longer overflows the stack the first time it is serialized.
- Code fixes (implementing
[NinoPooled]hooks, adding or removing attributes) keep the line breaks of the file they edit, instead of inserting the machine's (CRLF lines in an LF file on Windows). - Data crafted against a reader can no longer crash the process: a struct nested in itself thousands deep (through a list, nullable or interface member) throws instead of overflowing the stack.
- A failed read into an existing value no longer leaves an array of uninitialized memory in it, and a
StringBuilderor multi-dimensional array length is checked against the data before allocating. - Invalid
Version,Uri,CultureInfo,TimeZoneInfoandDateTimeOffsetvalues and null dictionary keys throw Nino'sInvalidOperationException(the original as its inner exception) instead of BCL exceptions, so Nino.AspNetCore answers them with 400. - Data that claims more elements than it holds can no longer make a read allocate hundreds of times its size: every collection allocates ahead of its elements at most 16 times the bytes left in the input, and grows as elements are actually read.
Every change between v4 releases: Changelog.
What's Changed since v4.0.0-rc.6
158 commits
✨ Features
- feat(unity): ship the analyzer's code fixes in the Unity package (d414f2d) by @JasonXuDeveloper
⚡ Performance (65)
- perf(core): serialize collections of fixed-size elements straight into an array of their size (72a0515) by @JasonXuDeveloper
- perf(core): move padded blocks with in-lane shuffles without AVX-512 (3e4ed67) by @JasonXuDeveloper
- perf(core): allocate arrays filled by vector or per-value stores zeroed (47a0baa) by @JasonXuDeveloper
- perf(core): write a length and its block of elements in one reservation (2b6ab85) by @JasonXuDeveloper
- perf(core): read and write a pair of raw items as one block (279f4f1) by @JasonXuDeveloper
- perf(core): skip clearing slots that a block read overwrites (83034b6) by @JasonXuDeveloper
- perf(core): allocate the arrays of Memory<T> and groupings as arrays are (87ee637) by @JasonXuDeveloper
- perf(core): copy mid-size blocks inline and read raw blocks with vector stores (6af50fa) by @JasonXuDeveloper
- perf(core): read and write multi-dimensional arrays and lists as blocks (84c40e2) by @JasonXuDeveloper
- perf(core): write reversed stacks as blocks and loop on local readers and writers (d66c123) by @JasonXuDeveloper
- perf(core): read a raw nullable's flag and value under one bounds check (e83c7c8) by @JasonXuDeveloper
- perf(core): reserve and read Version and the Unity types as whole blocks (3851e47) by @JasonXuDeveloper
- perf(immutable): write an ImmutableStack<T> without a list to grow and reverse (216c196) by @JasonXuDeveloper
- perf(core): write enumerated collections through a local writer (6d0cf4c) by @JasonXuDeveloper
- perf(generator): read a version-tolerant run of raw members as one block (667d91c) by @JasonXuDeveloper
- perf(generator): reserve a keyed member's key and region length together (d2062f1) by @JasonXuDeveloper
- perf(generator): inline a pooled type's rent and keep its warmup out of line (9505cd7) by @JasonXuDeveloper
- perf(core): write elements with references through the writer parameter (0a58c93) by @JasonXuDeveloper
- perf(core): keep one element loop per method in the collection writes (2862c0e) by @JasonXuDeveloper
- perf(core): call the copy of raw reads out of line (e02d23c) by @JasonXuDeveloper
- perf(core): keep rare paths out of the reads the JIT inlines into one method (dbd6c72) by @JasonXuDeveloper
- perf(core): write a string that is not Latin-1 out of line (85862a4) by @JasonXuDeveloper
- perf(generator): leave a pooled type's rent to the JIT's inlining heuristics (83a4dc5) by @JasonXuDeveloper
- perf(core): keep the element loops of collection reads and writes out of line (c00b0b7) by @JasonXuDeveloper
- perf(core): cover the ends of grouped block copies with whole groups (1c6a7aa) by @JasonXuDeveloper
- perf(core): drop checks the JIT cannot prove unneeded (1cc9c64) by @JasonXuDeveloper
- perf(core): allocate arrays uninitialized only from 16 KB (f5a0a99) by @JasonXuDeveloper
- perf(core): allocate arrays uninitialized from 6 KB, not 16 KB (417ab11) by @JasonXuDeveloper
- perf(core): allocate arrays uninitialized only from 6 KB (822b482) by @JasonXuDeveloper
- perf(core): native-sized offsets in the masked copy of padded structs (ce27d72) by @JasonXuDeveloper
- perf(core): copy padded structs on Arm64 with load and store pairs (c3ee83f) by @JasonXuDeveloper
- perf(core): copy with vector loops up to 32 KB, or 16 KB with 32-byte vectors (e1ef505) by @JasonXuDeveloper
- perf(core): share loads across a group's vectors on Arm64 (b181a92) by @JasonXuDeveloper
- perf(core): allocate overwritten arrays uninitialized on Arm64 and AMD (0a21a5f) by @JasonXuDeveloper
- perf(core): write set, queue and enumerated elements with references through the writer (70b6cc0) by @JasonXuDeveloper
- perf(core): Arm64 grid lane plans for reads only; new arrays zeroed again (78fc558) by @JasonXuDeveloper
- perf(core): bump lane cursors a group at a time (f4b3e64) by @JasonXuDeveloper
- perf(core): Arm64 reads look up each value from its own window (bd54659) by @JasonXuDeveloper
- perf(core): Arm64 lane groups load their grid windows once (0d3dc12) by @JasonXuDeveloper
- perf(core): Arm64 one-window lookups stay one-register (9f6b1a2) by @JasonXuDeveloper
- perf(core): Arm64 64-byte permutes keep their indices in registers (e7c854b) by @JasonXuDeveloper
- perf(core): Arm64 64-byte permutes look up only the quarters they use (5539158) by @JasonXuDeveloper
- perf(core): inline the AVX2 lane-permute check (a7e7e87) by @JasonXuDeveloper
- perf(core): move 5-byte blocks a group at a time without AVX-512 (7a8f0e9) by @JasonXuDeveloper
- perf(collections): write raw HashSet and Dictionary from their entries (f8c244e) by @JasonXuDeveloper
- perf(core): read small tuples on Arm64 with one-register lookups (b23c0ce) by @JasonXuDeveloper
- perf(core): keep grid reads on Arm64 where a step already moves four vectors (f957619) by @JasonXuDeveloper
- perf(generator): dispatch the subtypes of generic bases through generated code (c70bc9f) by @JasonXuDeveloper
- perf(generator): subtypes of a shared generic base write its members themselves (e6fb2fc) by @JasonXuDeveloper
- perf(generator): keep the bodies of shared generic bases' subtypes out of line (c1443b4) by @JasonXuDeveloper
- perf(core): copy blocks up to 1 MB with 32-byte vectors where AVX-512 is not used (8cc95e3) by @JasonXuDeveloper
- perf(immutable): build ImmutableDictionary and ImmutableHashSet as their tree (d89f2db) by @JasonXuDeveloper
- perf(immutable): build ImmutableSortedDictionary and ImmutableSortedSet as their tree (e6e81f2) by @JasonXuDeveloper
- perf(core): answer an empty pool without a call (9310097) by @JasonXuDeveloper
- perf(collections): build LinkedList, SortedSet and SortedDictionary as their nodes (d139f57) by @JasonXuDeveloper
- perf(immutable): write immutable sets and dictionaries copied out of their trees (4c4a2ef) by @JasonXuDeveloper
- perf(collections): write SortedSet and SortedDictionary copied out of their tree (d315579) by @JasonXuDeveloper
- perf(core): take from a pool that was never filled without a thread-local access (7018007) by @JasonXuDeveloper
- perf(collections): skip the element loops of empty collections (6bde97f) by @JasonXuDeveloper
- perf(collections): fill new hash sets and dictionaries of raw elements directly (da6d02b) by @JasonXuDeveloper
- perf(core): copy blocks up to 64 KB on Arm64 with paired vector loads from an aligned source (967a5f3) by @JasonXuDeveloper
- perf(core): write a raw subtype in the generated dispatch without a nested helper (866db82) by @JasonXuDeveloper
- perf(core): copy into a new array with vector stores on AMD x64 (12d7361) by @JasonXuDeveloper
- perf(core): copy small raw lists in place with Memmove (f0d03eb) by @JasonXuDeveloper
- perf(core): record the polymorphic array gains measured across rc.7 (a68ecd0) by @JasonXuDeveloper
🐛 Fixes
- fix(core): reject writes whose size overflows an int instead of writing past the buffer (af17feb) by @JasonXuDeveloper
- fix(benchmarks): call cases under 1 ns on par, and count only real wins in the ranges (8d56ec0) by @JasonXuDeveloper
- fix(generator): route open-generic polymorphic bases through runtime dispatch (3c01347) by doge
- fix(generator): dispatch generic polymorphic types through the shared entry points (c58d1ac) by @JasonXuDeveloper
- fix(core): make type registration re-entrant (1a70f60) by @JasonXuDeveloper
- fix(generator): code fixes keep the file's line breaks (3a8c355) by @JasonXuDeveloper
- fix(generator): code fixes give added usings and attributes the file's line breaks (02f7d53) by @JasonXuDeveloper
- fix(core): reject crafted data with Nino's exception, without a crash or stale memory (4b6a3a5) by @JasonXuDeveloper
- fix(core): bound what a read allocates per byte of the data (71497c7) by @JasonXuDeveloper
♻️ Refactoring and cleanup (23)
- refactor(generator): decide each symbol rule in one place (9070c60) by @JasonXuDeveloper
- refactor(generator): tell formatters apart by traits, not by name (d4572ed) by @JasonXuDeveloper
- refactor(generator): share the emitters' signatures and helpers, alias the interceptors' names (fd52664) by @JasonXuDeveloper
- refactor(generator): bind top-level calls once and register types through one record (c27ae16) by @JasonXuDeveloper
- refactor(core): share the struct formatter wrappers and registry rebuilds (d73ac0f) by @JasonXuDeveloper
- refactor(core): deduplicate the deferral contexts and internal helpers (9a424e7) by @JasonXuDeveloper
- refactor(core): share the null headers of NinoObject, tuples and nullables (3064936) by @JasonXuDeveloper
- refactor(core): share the prologues and helpers of the collection formatters (555ee7d) by @JasonXuDeveloper
- refactor(core): give the lanes a direction type and drop the group width (ade1dc2) by @JasonXuDeveloper
- refactor(core): allocate the strings Reader decodes from UTF-8 through NinoStrings (b3ac152) by @JasonXuDeveloper
- refactor(core): share the null header of Lazy<T> with sealed classes and tuples (dd09ab8) by @JasonXuDeveloper
- refactor(generator): drop the out-of-line bodies that inlined bodies left unused (c77dba2) by @JasonXuDeveloper
- refactor(generator): pass structs to the shallow write bodies by reference too (4b87a32) by @JasonXuDeveloper
- refactor(generator): open a keyed body's region with its first raw members (0f326fc) by @JasonXuDeveloper
- refactor(generator): keep an intercepted call's parts instead of its text (32e8694) by @JasonXuDeveloper
- refactor(generator): split the base prefix, name checks and dispatch root out of ModelBuilder.Build (5238a66) by @JasonXuDeveloper
- refactor(generator): let a sealed class's __Top twin be its shallow twin (ea5e553) by @JasonXuDeveloper
- refactor(benchmarks): read every report through one parser, bdn.py (f743b1b) by @JasonXuDeveloper
- refactor(benchmarks): compile v4's job and data into the v3 benchmark (2172856) by @JasonXuDeveloper
- refactor(core): size element blocks in 64 bits in one place (dbd4371) by @JasonXuDeveloper
- refactor(core): allocate uninitialized arrays through NewArray only (bd30f4f) by @JasonXuDeveloper
- refactor(generator): build the creation of a class to read into in one place (aa52511) by @JasonXuDeveloper
- refactor(generator): report an empty model name as one build error naming the type (9cd702a) by @JasonXuDeveloper
📖 Documentation (19)
- docs(benchmarks): describe the five shards and three processes of report.yml (9bc2b85) by @JasonXuDeveloper
- docs(benchmarks): list the copy, zeroing, lane and loop optimizations of rc.7 (4a51b7b) by @JasonXuDeveloper
- docs(benchmarks): apply the 1 ns floor to the published tables and fix stale protocol text (0dace5e) by @JasonXuDeveloper
- docs(benchmarks): describe the rc.7 machine-code work in the optimization list (8bcf058) by @JasonXuDeveloper
- docs(benchmarks): uninitialized arrays and memmove reads from 6 KB (f41c6c6) by @JasonXuDeveloper
- docs(benchmarks): describe the Arm64 lane loads, copy limits and writer loops of rc.7 (dbb0dd2) by @JasonXuDeveloper
- docs: note the vector loop pitfalls found on Arm64 (fbf2937) by @JasonXuDeveloper
- docs: note that Arm64 lookups take only the registers they use (6863db4) by @JasonXuDeveloper
- docs(benchmarks): describe the 64-byte permutes of small padded values (5a9d8c1) by @JasonXuDeveloper
- docs: inline small helpers of hot paths, and turn AVX-512 off for real (0578ea8) by @JasonXuDeveloper
- docs(benchmarks): describe 5-byte groups without AVX-512 and HashSet/Dictionary entries (9789cdc) by @JasonXuDeveloper
- docs(benchmarks): show a FILTER of several patterns (f806454) by @JasonXuDeveloper
- docs(benchmarks): describe the hash table fill and the empty-collection skip (770dff0) by @JasonXuDeveloper
- docs(readme): say up front that Nino reads untrusted data (a30e280) by @JasonXuDeveloper
- docs(readme): bring the README in line with the docs for rc.7 (b71b970) by @JasonXuDeveloper
- docs(readme): lead the MemoryPack comparison with untrusted input, list its bugs (5b80312) by @JasonXuDeveloper
- docs(readme): say plainly that MemoryPack supports Unity too (64773c1) by @JasonXuDeveloper
- docs(benchmarks): describe the AMD copy into new arrays and small lists read in place (13dcb4f) by @JasonXuDeveloper
- docs(benchmarks): drop local measurements from the rc.7 optimization list (38ce72f) by @JasonXuDeveloper
🧪 Tests (19)
- test(core): check sized serialization against the writer, and run the SSSE3 path in CI (5cf2422) by @JasonXuDeveloper
- test(benchmarks): verify every case before it is timed, and seed the data (f135d62) by @JasonXuDeveloper
- test(benchmarks): compare the JIT's code of two commits with asm.py (d2fdc70) by @JasonXuDeveloper
- test(benchmarks): probe zeroed against uninitialized arrays by size (438b88a) by @JasonXuDeveloper
- test(benchmarks): probe AVX-512 on/off and block copy against memmove (618f26f) by @JasonXuDeveloper
- test: runtime tests read a 1,000-value tuple array and list (78f7fff) by @JasonXuDeveloper
- test(benchmarks): run the probes only when a filter names one (55b9b05) by @JasonXuDeveloper
- test(benchmarks): probe lists of small padded tuples (8a4ba3b) by @JasonXuDeveloper
- test(benchmarks): probe one-register table lookups for tuple reads (db3a74b) by @JasonXuDeveloper
- test(benchmarks): probe 8 KB copies at every source offset (765329c) by @JasonXuDeveloper
- test(generator): compile and round-trip every combination of hierarchy shapes (1aa5dbc) by @JasonXuDeveloper
- test(benchmarks): probe generic against non-generic polymorphic bases (7aac475) by @JasonXuDeveloper
- test(benchmarks): measure generic polymorphic bases in the suite (05d28f0) by @JasonXuDeveloper
- test(generator): more closed instances of a generic base in the shape matrix (3fabb01) by @JasonXuDeveloper
- test: reference only managed assemblies from the runtime directory (952eae0) by @JasonXuDeveloper
- test(benchmarks): compare tuples field by field in --verify (6b2917d) by @JasonXuDeveloper
- test(benchmarks): add SortedSet and SortedDictionary to CollectionsTest (eb8d768) by @JasonXuDeveloper
- test(benchmarks): probe 16-byte vector copies against memcpy on Arm64 (2346d07) by @JasonXuDeveloper
- test(benchmarks): probe Arm64 vector copies by source alignment and size (7966dc3) by @JasonXuDeveloper
🔧 Build, CI and dependencies (22)
- ci(benchmarks): run the benchmarks at high priority (71d9876) by @JasonXuDeveloper
- ci(benchmarks): keep running at normal priority when the nice limit cannot be raised (c9eb470) by @JasonXuDeveloper
- chore: stop tracking Python bytecode (504a0d4) by @JasonXuDeveloper
- ci: define the benchmark settings, section headings and package ids once (05ab2d8) by @JasonXuDeveloper
- chore(benchmarks): probe tuple kernels and bisect the library on a runner (669fb27) by @JasonXuDeveloper
- ci(benchmarks): list the bisect checkouts before turning globbing off (7a1fd07) by @JasonXuDeveloper
- chore(benchmarks): probe Arm64 table lookups, read allocation and larger copies (0d38c4e) by @JasonXuDeveloper
- chore(benchmarks): measure the Arm64 grid plans and uninitialized arrays apart (4cce26e) by @JasonXuDeveloper
- ci(benchmarks): skip the release checkout when bisecting (662613c) by @JasonXuDeveloper
- ci: run the unit tests on Windows too (e51cab1) by @JasonXuDeveloper
- chore(benchmarks): let run.sh take several filter patterns (23756f6) by @JasonXuDeveloper
- ci: compare A/B benchmarks against main as well as the previous release (6d77818) by @JasonXuDeveloper
- ci(deps): bump actions/setup-node from 4 to 7 (#204) (a1c253a)
- build(deps): bump System.Reflection.MetadataLoadContext from 8.0.1 to 10.0.13 (#205) (f50828b)
- ci(benchmarks): compare against a release that cannot compile every benchmark (48d95a7) by @JasonXuDeveloper
- ci(benchmarks): give BenchmarkDotNet's own build of a legacy base the NinoBenchLegacy property (0b36c80) by @JasonXuDeveloper
- ci(benchmarks): run the A/B benchmarks in report.yml's shards, in parallel (808ddcc) by @JasonXuDeveloper
- ci(benchmarks): keep an older release's expected build errors out of the run's annotations (5fb0eb6) by @JasonXuDeveloper
- ci(benchmarks): upload only the merged reports from the A/B summary (be75c43) by @JasonXuDeveloper
- ci(benchmarks): summarize a filter run, whose one shard download-artifact does not put in a directory (bdda1b5) by @JasonXuDeveloper
- chore(benchmarks): probe a read into a new array, Memmove against the vector copy (02e2a52) by @JasonXuDeveloper
- chore(benchmarks): probe copies of 160 KB, the size of the Vectors case (18d0b3c) by @JasonXuDeveloper
Contributors
Thanks to everyone who contributed to this release: @JasonXuDeveloper, doge
Full Changelog: v4.0.0-rc.6...v4.0.0-rc.7
📊 v3 3.3.13 vs v4 (same machine)
Nino v3 3.3.13 (NuGet) against v4.0.0-rc.7, back to back on the same runner
benchmarks/v3 (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, on the same runner, 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 | 124.71 ns | 69.59 ns | 0.56x | 291.13 ns | 300.15 ns |
| SerializeSimpleClasses | 17.98 μs | 7.84 μs | 0.44x | 27.39 μs | 29.56 μs |
| SerializeSimpleStruct | 1.28 ns | 0.76 ns | faster (v4 too fast to measure) | 2.44 ns | 2.55 ns |
| SerializeSimpleStructs | 13.20 ns | 11.71 ns | 0.89x | 14.25 ns | 13.97 ns |
| SerializeVectors | 3.64 μs | 3.57 μs | 0.98x | 3.53 μs | 3.57 μs |
| DeserializeSimpleClass | 442.61 ns | 366.34 ns | 0.83x | 514.45 ns | 564.16 ns |
| DeserializeSimpleClasses | 47.64 μs | 39.24 μs | 0.82x | 51.12 μs | 54.98 μs |
| DeserializeSimpleStruct | 0.36 ns | 0.34 ns | on par (both too fast to measure) | 0.33 ns | 0.33 ns |
| DeserializeSimpleStructs | 81.97 ns | 76.92 ns | 0.94x | 88.65 ns | 90.76 ns |
| DeserializeVectors | 97.46 μs | 96.22 μs | 0.99x | 95.26 μs | 102.20 μs |
v4 vs v3 = mean(v4) / mean(v3); below 1.00x means v4 is faster.
MemoryPack drift (v4 run / v3 run): min 0.91x, max 1.02x (same code in both runs; the spread is the noise floor of this comparison).
v3 3.3.13 runner
BenchmarkDotNet v0.15.8, Linux Ubuntu 26.04.1 LTS (Resolute Raccoon)
INTEL XEON PLATINUM 8573C 3.18GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.402
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
ShortRun-.NET 10.0 : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
Job=ShortRun-.NET 10.0 Runtime=.NET 10.0 Server=True
IterationCount=15 LaunchCount=3 WarmupCount=5
The v3 report: benchmark-v3-full.md.
📊 Arm64 benchmark
The same benchmarks of v4.0.0-rc.7 on an Arm64 runner
The benchmark classes of the x64 report below, with the same BenchmarkDotNet settings
(--launchCount 3 --warmupCount 5 --iterationCount 15), on GitHub's ubuntu-24.04-arm runners. Compare Nino with MemoryPack within
this table: the runners differ from the x64 ones, so times across the two tables say little.
BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
Neoverse-N2, 4 physical cores
.NET SDK 10.0.402
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), Arm64 RyuJIT armv8.0-a
ShortRun-.NET 10.0 : .NET 10.0.13 (10.0.13, 10.0.1326.50504), Arm64 RyuJIT armv8.0-a
Job=ShortRun-.NET 10.0 Runtime=.NET 10.0 Server=True
IterationCount=15 LaunchCount=3 WarmupCount=5
PooledTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeNew | 500.67 ns | 1.00 | - | 2536 B |
| NinoDeserializePooled | 287.58 ns | 0.57 | - | - |
| MemoryPackDeserializeNew | 747.96 ns | 1.49 | - | 2536 B |
| MemoryPackDeserializeOverwrite | 546.36 ns | 1.09 | - | 1080 B |
SimpleTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeSimpleClass | 504.39 ns | 1.00 | - | 2528 B |
| MemoryPackDeserializeSimpleClass | 756.66 ns | 1.50 | - | 2528 B |
| NinoSerializeSimpleClass | 105.52 ns | 1.00 | 1,104 B | - |
| MemoryPackSerializeSimpleClass | 453.24 ns | 4.30 | 1,148 B | - |
| NinoDeserializeSimpleClasses | 49.53 μs | 1.00 | - | 253624 B |
| MemoryPackDeserializeSimpleClasses | 68.98 μs | 1.39 | - | 253624 B |
| NinoSerializeSimpleClasses | 11.13 μs | 1.00 | 110,404 B | - |
| MemoryPackSerializeSimpleClasses | 44.89 μs | 4.03 | 114,804 B | - |
| NinoDeserializeSimpleStruct | 4.69 ns | 1.00 | - | - |
| MemoryPackDeserializeSimpleStruct | 4.71 ns | 1.00 | - | - |
| NinoSerializeSimpleStruct | 1.88 ns | 1.00 | 12 B | - |
| MemoryPackSerializeSimpleStruct | 4.87 ns | 2.59 | 12 B | - |
| NinoDeserializeSimpleStructs | 114.55 ns | 1.00 | - | 1224 B |
| MemoryPackDeserializeSimpleStructs | 139.42 ns | 1.22 | - | 1224 B |
| NinoSerializeSimpleStructs | 21.13 ns | 1.00 | 1,204 B | - |
| MemoryPackSerializeSimpleStructs | 29.41 ns | 1.39 | 1,204 B | - |
| NinoDeserializeVectors | 104.84 μs | 1.00 | - | 160024 B |
| MemoryPackDeserializeVectors | 107.92 μs | 1.03 | - | 160026 B |
| NinoSerializeVectors | 3.27 μs | 1.00 | 160,004 B | - |
| MemoryPackSerializeVectors | 3.25 μs | 0.99 | 160,004 B | - |
FixedSizeTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeFixedClass | 9.85 ns | 1.00 | - | 56 B |
| MemoryPackDeserializeFixedClass | 29.32 ns | 2.98 | - | 56 B |
| NinoSerializeFixedClass | 11.32 ns | 1.00 | 41 B | 72 B |
| MemoryPackSerializeFixedClass | 30.57 ns | 2.70 | 41 B | 72 B |
| NinoDeserializeFixedClasses | 1.15 μs | 1.00 | - | 6424 B |
| MemoryPackDeserializeFixedClasses | 1.57 μs | 1.37 | - | 6424 B |
| NinoSerializeFixedClasses | 622.83 ns | 1.00 | 4,104 B | 4128 B |
| MemoryPackSerializeFixedClasses | 1.86 μs | 2.98 | 4,104 B | 4128 B |
| NinoDeserializeFixedStruct | 0.52 ns | 1.00 | - | - |
| MemoryPackDeserializeFixedStruct | 1.17 ns | 2.24 | - | - |
| NinoSerializeFixedStruct | 9.71 ns | 1.00 | 40 B | 64 B |
| MemoryPackSerializeFixedStruct | 9.77 ns | 1.01 | 40 B | 64 B |
CommonTypesTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeInventory | 621.39 ns | 1.00 | - | 8024 B |
| MemoryPackDeserializeInventory | 654.02 ns | 1.05 | - | 8024 B |
| NinoSerializeInventory | 129.09 ns | 1.00 | 8,004 B | - |
| MemoryPackSerializeInventory | 140.14 ns | 1.09 | 8,004 B | - |
| NinoDeserializeOrders | 25.94 μs | 1.00 | - | 88552 B |
| MemoryPackDeserializeOrders | 36.14 μs | 1.39 | - | 88552 B |
| NinoSerializeOrders | 7.29 μs | 1.00 | 26,126 B | - |
| MemoryPackSerializeOrders | 14.56 μs | 2.00 | 27,990 B | - |
| NinoDeserializePlayers | 32.85 μs | 1.00 | - | 120056 B |
| MemoryPackDeserializePlayers | 45.47 μs | 1.38 | - | 120056 B |
| NinoSerializePlayers | 9.55 μs | 1.00 | 39,164 B | - |
| MemoryPackSerializePlayers | 27.29 μs | 2.86 | 43,364 B | - |
| NinoDeserializeShapes | 11.01 μs | 1.00 | - | 36056 B |
| MemoryPackDeserializeShapes | 15.57 μs | 1.41 | - | 36056 B |
| NinoSerializeShapes | 1.93 μs | 1.00 | 14,004 B | - |
| MemoryPackSerializeShapes | 25.51 μs | 13.22 | 12,004 B | - |
| NinoDeserializeStringDictionary | 40.85 μs | 1.00 | - | 110216 B |
| MemoryPackDeserializeStringDictionary | 60.50 μs | 1.48 | - | 110216 B |
| NinoSerializeStringDictionary | 8.54 μs | 1.00 | 22,734 B | - |
| MemoryPackSerializeStringDictionary | 35.68 μs | 4.18 | 30,734 B | - |
| NinoDeserializeTree | 52.10 μs | 1.00 | - | 171872 B |
| MemoryPackDeserializeTree | 69.22 μs | 1.33 | - | 171872 B |
| NinoSerializeTree | 15.61 μs | 1.00 | 31,258 B | - |
| MemoryPackSerializeTree | 21.53 μs | 1.38 | 32,623 B | - |
GenericPolymorphicTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeGenericIntBase | 13.03 μs | 1.00 | - | 32024 B |
| MemoryPackDeserializeGenericIntBase | 46.29 μs | 3.55 | - | 32024 B |
| NinoSerializeGenericIntBase | 3.03 μs | 1.00 | - | - |
| MemoryPackSerializeGenericIntBase | 56.10 μs | 18.50 | - | - |
| NinoDeserializeGenericManagedTupleBase | 37.63 μs | 1.00 | - | 72024 B |
| MemoryPackDeserializeGenericManagedTupleBase | 80.42 μs | 2.14 | - | 72024 B |
| NinoSerializeGenericManagedTupleBase | 9.38 μs | 1.00 | - | - |
| MemoryPackSerializeGenericManagedTupleBase | 106.51 μs | 11.36 | - | - |
| NinoDeserializeGenericOpenClassTupleBase | 31.51 μs | 1.00 | - | 72024 B |
| MemoryPackDeserializeGenericOpenClassTupleBase | 70.26 μs | 2.23 | - | 72024 B |
| NinoSerializeGenericOpenClassTupleBase | 7.71 μs | 1.00 | - | - |
| MemoryPackSerializeGenericOpenClassTupleBase | 109.69 μs | 14.23 | - | - |
| NinoDeserializeGenericSealedClassTupleBase | 31.65 μs | 1.00 | - | 72024 B |
| MemoryPackDeserializeGenericSealedClassTupleBase | 70.32 μs | 2.22 | - | 72024 B |
| NinoSerializeGenericSealedClassTupleBase | 8.11 μs | 1.00 | - | - |
| MemoryPackSerializeGenericSealedClassTupleBase | 109.04 μs | 13.48 | - | - |
| NinoDeserializeGenericUnmanagedTupleBase | 18.37 μs | 1.00 | - | 40024 B |
| MemoryPackDeserializeGenericUnmanagedTupleBase | 45.70 μs | 2.49 | - | 40024 B |
| NinoSerializeGenericUnmanagedTupleBase | 5.29 μs | 1.00 | - | - |
| MemoryPackSerializeGenericUnmanagedTupleBase | 62.57 μs | 11.82 | - | - |
| NinoDeserializeNonGenericBase | 12.06 μs | 1.00 | - | 32024 B |
| MemoryPackDeserializeNonGenericBase | 22.39 μs | 1.86 | - | 32024 B |
| NinoSerializeNonGenericBase | 3.11 μs | 1.00 | - | - |
| MemoryPackSerializeNonGenericBase | 38.39 μs | 12.35 | - | - |
VersionTolerantTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeTolerantClass | 508.20 ns | 1.00 | - | 2528 B |
| MemoryPackDeserializeTolerantClass | 764.00 ns | 1.50 | - | 2528 B |
| NinoSerializeTolerantClass | 101.10 ns | 1.00 | 1,108 B | - |
| MemoryPackSerializeTolerantClass | 614.50 ns | 6.08 | 1,164 B | - |
| NinoDeserializeTolerantClasses | 51.77 μs | 1.00 | - | 253624 B |
| MemoryPackDeserializeTolerantClasses | 76.08 μs | 1.47 | - | 253624 B |
| NinoSerializeTolerantClasses | 12.14 μs | 1.00 | 110,804 B | - |
| MemoryPackSerializeTolerantClasses | 61.43 μs | 5.06 | 116,404 B | - |
BclTypesTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeImmutableDictionary | 21.81 μs | 1.00 | - | 68088 B |
| MemoryPackDeserializeImmutableDictionary | 87.83 μs | 4.03 | - | 56096 B |
| NinoSerializeImmutableDictionary | 3.38 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeImmutableDictionary | 17.41 μs | 5.14 | 8,004 B | - |
| NinoDeserializeImmutableHashSet | 21.10 μs | 1.00 | - | 64096 B |
| MemoryPackDeserializeImmutableHashSet | 87.47 μs | 4.15 | - | 56104 B |
| NinoSerializeImmutableHashSet | 3.14 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableHashSet | 27.15 μs | 8.64 | 4,004 B | - |
| NinoDeserializeImmutableQueue | 7.78 μs | 1.00 | - | 36064 B |
| MemoryPackDeserializeImmutableQueue | 10.98 μs | 1.41 | - | 32112 B |
| NinoSerializeImmutableQueue | 1.45 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableQueue | 10.20 μs | 7.05 | 4,004 B | - |
| NinoDeserializeImmutableSortedDictionary | 14.59 μs | 1.00 | - | 56072 B |
| MemoryPackDeserializeImmutableSortedDictionary | 81.43 μs | 5.58 | - | 48112 B |
| NinoSerializeImmutableSortedDictionary | 3.12 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeImmutableSortedDictionary | 8.19 μs | 2.62 | 8,004 B | - |
| NinoDeserializeImmutableSortedSet | 14.00 μs | 1.00 | - | 52056 B |
| MemoryPackDeserializeImmutableSortedSet | 76.02 μs | 5.43 | - | 48088 B |
| NinoSerializeImmutableSortedSet | 2.59 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableSortedSet | 20.02 μs | 7.72 | 4,004 B | - |
| NinoDeserializeImmutableStack | 7.78 μs | 1.00 | - | 36024 B |
| MemoryPackDeserializeImmutableStack | 11.11 μs | 1.43 | - | 32040 B |
| NinoSerializeImmutableStack | 2.65 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableStack | 16.68 μs | 6.29 | 4,004 B | - |
| NinoDeserializeBigInteger | 40.36 ns | 1.00 | - | 256 B |
| NinoSerializeBigInteger | 12.67 ns | 1.00 | 203 B | - |
| NinoDeserializeBitArray | 56.21 ns | 1.00 | - | 336 B |
| NinoSerializeBitArray | 25.40 ns | 1.00 | 129 B | - |
| NinoDeserializeBlockingCollection | 11.59 μs | 1.00 | - | 12984 B |
| MemoryPackDeserializeBlockingCollection | 53.98 μs | 4.66 | - | - |
| NinoSerializeBlockingCollection | 5.36 μs | 1.00 | 4,004 B | 4096 B |
| MemoryPackSerializeBlockingCollection | 15.66 μs | 2.92 | 4,004 B | 72 B |
| NinoDeserializeCultureInfo | 63.29 ns | 1.00 | - | 64 B |
| MemoryPackDeserializeCultureInfo | 84.91 ns | 1.34 | - | 64 B |
| NinoSerializeCultureInfo | 4.25 ns | 1.00 | 9 B | - |
| MemoryPackSerializeCultureInfo | 31.25 ns | 7.35 | 13 B | - |
| NinoDeserializeLazy | 23.71 ns | 1.00 | - | 112 B |
| MemoryPackDeserializeLazy | 46.53 ns | 1.96 | - | 112 B |
| NinoSerializeLazy | 6.24 ns | 1.00 | 27 B | - |
| MemoryPackSerializeLazy | 55.76 ns | 8.93 | 31 B | - |
| NinoDeserializeLookup | 720.10 ns | 1.00 | - | 5152 B |
| MemoryPackDeserializeLookup | 1.01 μs | 1.40 | - | 5576 B |
| NinoSerializeLookup | 2.46 μs | 1.00 | 4,084 B | 320 B |
| MemoryPackSerializeLookup | 13.64 μs | 5.55 | 4,094 B | - |
| NinoDeserializeMemory | 357.85 ns | 1.00 | - | 4056 B |
| MemoryPackDeserializeMemory | 438.43 ns | 1.23 | - | 4056 B |
| NinoSerializeMemory | 58.06 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeMemory | 101.07 ns | 1.74 | 4,004 B | - |
| NinoDeserializeReadOnlyCollection | 348.87 ns | 1.00 | - | 4048 B |
| MemoryPackDeserializeReadOnlyCollection | 436.55 ns | 1.25 | - | 4048 B |
| NinoSerializeReadOnlyCollection | 2.64 μs | 1.00 | 4,004 B | 40 B |
| MemoryPackSerializeReadOnlyCollection | 13.72 μs | 5.20 | 4,004 B | 40 B |
| NinoDeserializeReadOnlySequence | 349.92 ns | 1.00 | - | 4064 B |
| MemoryPackDeserializeReadOnlySequence | 437.62 ns | 1.25 | - | 4064 B |
| NinoSerializeReadOnlySequence | 66.06 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeReadOnlySequence | 102.79 ns | 1.56 | 4,004 B | - |
| NinoDeserializeStringBuilder | 202.37 ns | 1.00 | - | 2072 B |
| MemoryPackDeserializeStringBuilder | 220.65 ns | 1.09 | - | 2072 B |
| NinoSerializeStringBuilder | 34.97 ns | 1.00 | 2,004 B | - |
| MemoryPackSerializeStringBuilder | 70.68 ns | 2.02 | 2,004 B | - |
| NinoDeserializeTimeZoneInfo | 14.56 ns | 1.00 | - | 32 B |
| MemoryPackDeserializeTimeZoneInfo | 335.77 ns | 23.07 | - | 680 B |
| NinoSerializeTimeZoneInfo | 11.78 ns | 1.00 | 11 B | - |
| MemoryPackSerializeTimeZoneInfo | 287.45 ns | 24.40 | 102 B | 216 B |
| NinoDeserializeUri | 100.14 ns | 1.00 | - | 184 B |
| MemoryPackDeserializeUri | 124.52 ns | 1.24 | - | 184 B |
| NinoSerializeUri | 8.37 ns | 1.00 | 56 B | - |
| MemoryPackSerializeUri | 36.85 ns | 4.40 | 60 B | - |
| NinoDeserializeVersion | 7.82 ns | 1.00 | - | 32 B |
| MemoryPackDeserializeVersion | 23.32 ns | 2.98 | - | 32 B |
| NinoSerializeVersion | 2.89 ns | 1.00 | 16 B | - |
| MemoryPackSerializeVersion | 28.95 ns | 10.01 | 17 B | - |
PolymorphicTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializePolymorphicClassArray | 197.82 μs | 1.00 | - | 745624 B |
| MemoryPackDeserializePolymorphicClassArray | 331.67 μs | 1.68 | - | 745624 B |
| NinoSerializePolymorphicClassArray | 59.06 μs | 1.00 | 397,304 B | - |
| MemoryPackSerializePolymorphicClassArray | 375.65 μs | 6.37 | 384,404 B | - |
| NinoDeserializePolymorphicClass | 19.73 μs | 1.00 | - | 79688 B |
| MemoryPackDeserializePolymorphicClass | 30.53 μs | 1.55 | - | 79688 B |
| NinoSerializePolymorphicClass | 6.44 μs | 1.00 | 53,789 B | - |
| MemoryPackSerializePolymorphicClass | 33.45 μs | 5.20 | 51,760 B | - |
| NinoDeserializePolymorphicFooArray | 18.79 μs | 1.00 | - | 76024 B |
| MemoryPackDeserializePolymorphicFooArray | 33.05 μs | 1.76 | - | 76024 B |
| NinoSerializePolymorphicFooArray | 6.61 μs | 1.00 | 52,404 B | - |
| MemoryPackSerializePolymorphicFooArray | 31.75 μs | 4.80 | 50,404 B | - |
PooledGraphTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoLeavesNew | 3.06 μs | 1.00 | - | 13656 B |
| NinoLeavesPooledElements | 3.06 μs | 1.00 | - | 856 B |
| NinoLeavesInPlace | 437.02 ns | 0.14 | - | - |
| MemoryPackLeavesNew | 3.89 μs | 1.27 | - | 13656 B |
| MemoryPackLeavesOverwrite | 4.23 μs | 1.38 | - | 12800 B |
| NinoNodeNew | 484.02 ns | 1.00 | - | 2192 B |
| NinoNodePooled | 144.74 ns | 0.30 | - | - |
| MemoryPackNodeNew | 663.61 ns | 1.37 | - | 2192 B |
| MemoryPackNodeOverwrite | 557.94 ns | 1.15 | - | 1024 B |
| NinoPlainNew | 2.87 μs | 1.00 | - | 12856 B |
| MemoryPackPlainNew | 3.80 μs | 1.33 | - | 12856 B |
CollectionsTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeConcurrentDictionary | 18.85 μs | 1.00 | - | 49184 B |
| MemoryPackDeserializeConcurrentDictionary | 67.56 μs | 3.58 | - | 141032 B |
| NinoSerializeConcurrentDictionary | 2.28 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeConcurrentDictionary | 3.15 μs | 1.38 | 8,004 B | - |
| NinoDeserializeHashSet | 2.84 μs | 1.00 | - | 17768 B |
| MemoryPackDeserializeHashSet | 6.45 μs | 2.27 | - | 17768 B |
| NinoSerializeHashSet | 373.33 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeHashSet | 13.11 μs | 35.11 | 4,004 B | - |
| NinoDeserializeKeyValuePairs | 659.38 ns | 1.00 | - | 8024 B |
| MemoryPackDeserializeKeyValuePairs | 682.21 ns | 1.03 | - | 8024 B |
| NinoSerializeKeyValuePairs | 110.07 ns | 1.00 | 8,004 B | - |
| MemoryPackSerializeKeyValuePairs | 141.26 ns | 1.28 | 8,004 B | - |
| NinoDeserializeLinkedList | 12.19 μs | 1.00 | - | 48040 B |
| MemoryPackDeserializeLinkedList | 15.02 μs | 1.23 | - | 48040 B |
| NinoSerializeLinkedList | 1.34 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeLinkedList | 14.05 μs | 10.45 | 4,004 B | - |
| NinoDeserializeQueue | 359.63 ns | 1.00 | - | 4064 B |
| MemoryPackDeserializeQueue | 3.90 μs | 10.84 | - | 4064 B |
| NinoSerializeQueue | 64.16 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeQueue | 12.78 μs | 199.23 | 4,004 B | - |
| NinoDeserializeSortedDictionary | 16.63 μs | 1.00 | - | 48112 B |
| MemoryPackDeserializeSortedDictionary | 44.98 μs | 2.70 | - | 48112 B |
| NinoSerializeSortedDictionary | 3.00 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeSortedDictionary | 6.04 μs | 2.01 | 8,004 B | 200 B |
| NinoDeserializeSortedList | 2.42 μs | 1.00 | - | 8112 B |
| MemoryPackDeserializeSortedList | 15.07 μs | 6.22 | - | - |
| NinoSerializeSortedList | 2.04 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeSortedList | 3.29 μs | 1.61 | 8,004 B | - |
| NinoDeserializeSortedSet | 14.71 μs | 1.00 | - | 40048 B |
| MemoryPackDeserializeSortedSet | 40.40 μs | 2.75 | - | 40048 B |
| NinoSerializeSortedSet | 2.85 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeSortedSet | 16.25 μs | 5.70 | 4,004 B | 200 B |
| NinoDeserializeTupleList | 1.38 μs | 1.00 | - | 16056 B |
| MemoryPackDeserializeTupleList | 1.41 μs | 1.03 | - | 16056 B |
| NinoSerializeTupleList | 219.51 ns | 1.00 | 12,004 B | - |
| MemoryPackSerializeTupleList | 291.79 ns | 1.33 | 16,004 B | - |
ReferenceTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeGraph | 6.47 μs | 1.00 | - | 14104 B |
| MemoryPackDeserializeGraph | 18.82 μs | 2.91 | - | 14104 B |
| NinoSerializeGraph | 4.37 μs | 1.00 | 5,878 B | - |
| MemoryPackSerializeGraph | 32.04 μs | 7.34 | 5,584 B | - |
The classes ran in parallel, each on its own runner (a class's Nino and MemoryPack cases on one):
- PooledTest, SimpleTest: Neoverse-N2, 4 physical cores
- CommonTypesTest, FixedSizeTest: Neoverse-N2, 4 physical cores
- GenericPolymorphicTest, VersionTolerantTest: Neoverse-N2, 4 physical cores
- BclTypesTest, PolymorphicTest: Neoverse-N2, 4 physical cores
- BclTypesTest, PooledGraphTest: Neoverse-N2, 4 physical cores
- BclTypesTest, CollectionsTest: Neoverse-N2, 4 physical cores
- CollectionsTest, ReferenceTest: Neoverse-N2, 4 physical cores
Median, Min, Max and the GC counts: benchmark-arm64-full.md.
📊 Performance Report
Click to expand benchmark results
BenchmarkDotNet v0.15.8, Linux Ubuntu 26.04.1 LTS (Resolute Raccoon)
INTEL XEON PLATINUM 8573C 3.50GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.402
[Host] : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
ShortRun-.NET 10.0 : .NET 10.0.13 (10.0.13, 10.0.1326.50504), X64 RyuJIT x86-64-v4
Job=ShortRun-.NET 10.0 Runtime=.NET 10.0 Server=True
IterationCount=15 LaunchCount=3 WarmupCount=5
PooledTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeNew | 390.80 ns | 1.00 | - | 2536 B |
| NinoDeserializePooled | 181.10 ns | 0.46 | - | - |
| MemoryPackDeserializeNew | 523.45 ns | 1.34 | - | 2536 B |
| MemoryPackDeserializeOverwrite | 443.44 ns | 1.14 | - | 1080 B |
SimpleTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeSimpleClass | 366.34 ns | 1.00 | - | 2528 B |
| MemoryPackDeserializeSimpleClass | 514.45 ns | 1.40 | - | 2528 B |
| NinoSerializeSimpleClass | 69.59 ns | 1.00 | 1,104 B | - |
| MemoryPackSerializeSimpleClass | 291.13 ns | 4.19 | 1,148 B | - |
| NinoDeserializeSimpleClasses | 39.24 μs | 1.00 | - | 253624 B |
| MemoryPackDeserializeSimpleClasses | 51.12 μs | 1.30 | - | 253624 B |
| NinoSerializeSimpleClasses | 7.84 μs | 1.00 | 110,404 B | - |
| MemoryPackSerializeSimpleClasses | 27.39 μs | 3.49 | 114,804 B | - |
| NinoDeserializeSimpleStruct | 0.34 ns | 1.00 | - | - |
| MemoryPackDeserializeSimpleStruct | 0.33 ns | 0.99 | - | - |
| NinoSerializeSimpleStruct | 0.76 ns | 1.01 | 12 B | - |
| MemoryPackSerializeSimpleStruct | 2.44 ns | 3.24 | 12 B | - |
| NinoDeserializeSimpleStructs | 76.92 ns | 1.00 | - | 1224 B |
| MemoryPackDeserializeSimpleStructs | 88.65 ns | 1.15 | - | 1224 B |
| NinoSerializeSimpleStructs | 11.71 ns | 1.00 | 1,204 B | - |
| MemoryPackSerializeSimpleStructs | 14.25 ns | 1.22 | 1,204 B | - |
| NinoDeserializeVectors | 96.22 μs | 1.00 | - | 160028 B |
| MemoryPackDeserializeVectors | 95.26 μs | 0.99 | - | 160031 B |
| NinoSerializeVectors | 3.57 μs | 1.00 | 160,004 B | - |
| MemoryPackSerializeVectors | 3.53 μs | 0.99 | 160,004 B | - |
FixedSizeTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeFixedClass | 8.06 ns | 1.00 | - | 56 B |
| MemoryPackDeserializeFixedClass | 22.80 ns | 2.83 | - | 56 B |
| NinoSerializeFixedClass | 8.76 ns | 1.00 | 41 B | 72 B |
| MemoryPackSerializeFixedClass | 24.77 ns | 2.83 | 41 B | 72 B |
| NinoDeserializeFixedClasses | 1.02 μs | 1.00 | - | 6424 B |
| MemoryPackDeserializeFixedClasses | 1.38 μs | 1.35 | - | 6424 B |
| NinoSerializeFixedClasses | 562.76 ns | 1.00 | 4,104 B | 4128 B |
| MemoryPackSerializeFixedClasses | 1.75 μs | 3.11 | 4,104 B | 4128 B |
| NinoDeserializeFixedStruct | 0.29 ns | 1.01 | - | - |
| MemoryPackDeserializeFixedStruct | 1.08 ns | 3.73 | - | - |
| NinoSerializeFixedStruct | 7.88 ns | 1.00 | 40 B | 64 B |
| MemoryPackSerializeFixedStruct | 8.02 ns | 1.02 | 40 B | 64 B |
CommonTypesTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeInventory | 390.00 ns | 1.00 | - | 8024 B |
| MemoryPackDeserializeInventory | 415.95 ns | 1.07 | - | 8024 B |
| NinoSerializeInventory | 136.52 ns | 1.03 | 8,004 B | - |
| MemoryPackSerializeInventory | 105.45 ns | 0.80 | 8,004 B | - |
| NinoDeserializeOrders | 24.29 μs | 1.00 | - | 88552 B |
| MemoryPackDeserializeOrders | 32.87 μs | 1.36 | - | 88552 B |
| NinoSerializeOrders | 7.78 μs | 1.00 | 26,126 B | - |
| MemoryPackSerializeOrders | 14.84 μs | 1.91 | 27,990 B | - |
| NinoDeserializePlayers | 32.71 μs | 1.00 | - | 120056 B |
| MemoryPackDeserializePlayers | 41.94 μs | 1.29 | - | 120056 B |
| NinoSerializePlayers | 8.82 μs | 1.00 | 39,164 B | - |
| MemoryPackSerializePlayers | 25.12 μs | 2.85 | 43,364 B | - |
| NinoDeserializeShapes | 10.77 μs | 1.00 | - | 36056 B |
| MemoryPackDeserializeShapes | 16.56 μs | 1.54 | - | 36056 B |
| NinoSerializeShapes | 2.04 μs | 1.00 | 14,004 B | - |
| MemoryPackSerializeShapes | 22.85 μs | 11.23 | 12,004 B | - |
| NinoDeserializeStringDictionary | 31.81 μs | 1.00 | - | 110216 B |
| MemoryPackDeserializeStringDictionary | 54.07 μs | 1.70 | - | 110216 B |
| NinoSerializeStringDictionary | 8.69 μs | 1.00 | 22,734 B | - |
| MemoryPackSerializeStringDictionary | 32.73 μs | 3.77 | 30,734 B | - |
| NinoDeserializeTree | 53.04 μs | 1.00 | - | 171872 B |
| MemoryPackDeserializeTree | 66.70 μs | 1.26 | - | 171872 B |
| NinoSerializeTree | 16.38 μs | 1.00 | 31,258 B | - |
| MemoryPackSerializeTree | 20.04 μs | 1.22 | 32,623 B | - |
GenericPolymorphicTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeGenericIntBase | 11.04 μs | 1.00 | - | 32024 B |
| MemoryPackDeserializeGenericIntBase | 37.03 μs | 3.36 | - | - |
| NinoSerializeGenericIntBase | 2.75 μs | 1.00 | - | - |
| MemoryPackSerializeGenericIntBase | 41.32 μs | 15.01 | - | - |
| NinoDeserializeGenericManagedTupleBase | 24.06 μs | 1.00 | - | 72024 B |
| MemoryPackDeserializeGenericManagedTupleBase | 59.01 μs | 2.46 | - | - |
| NinoSerializeGenericManagedTupleBase | 16.99 μs | 1.00 | - | - |
| MemoryPackSerializeGenericManagedTupleBase | 74.47 μs | 4.38 | - | - |
| NinoDeserializeGenericOpenClassTupleBase | 21.56 μs | 1.01 | - | 72024 B |
| MemoryPackDeserializeGenericOpenClassTupleBase | 54.39 μs | 2.54 | - | 72024 B |
| NinoSerializeGenericOpenClassTupleBase | 14.56 μs | 1.02 | - | - |
| MemoryPackSerializeGenericOpenClassTupleBase | 75.94 μs | 5.29 | - | - |
| NinoDeserializeGenericSealedClassTupleBase | 20.72 μs | 1.00 | - | 72024 B |
| MemoryPackDeserializeGenericSealedClassTupleBase | 52.81 μs | 2.56 | - | 72024 B |
| NinoSerializeGenericSealedClassTupleBase | 16.03 μs | 1.07 | - | - |
| MemoryPackSerializeGenericSealedClassTupleBase | 73.28 μs | 4.89 | - | - |
| NinoDeserializeGenericUnmanagedTupleBase | 12.80 μs | 1.00 | - | 40024 B |
| MemoryPackDeserializeGenericUnmanagedTupleBase | 38.99 μs | 3.05 | - | - |
| NinoSerializeGenericUnmanagedTupleBase | 4.69 μs | 1.00 | - | - |
| MemoryPackSerializeGenericUnmanagedTupleBase | 45.30 μs | 9.66 | - | - |
| NinoDeserializeNonGenericBase | 9.84 μs | 1.00 | - | 32024 B |
| MemoryPackDeserializeNonGenericBase | 21.78 μs | 2.21 | - | 32024 B |
| NinoSerializeNonGenericBase | 2.82 μs | 1.00 | - | - |
| MemoryPackSerializeNonGenericBase | 27.33 μs | 9.71 | - | - |
VersionTolerantTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeTolerantClass | 327.50 ns | 1.01 | - | 2528 B |
| MemoryPackDeserializeTolerantClass | 536.17 ns | 1.65 | - | 2528 B |
| NinoSerializeTolerantClass | 76.06 ns | 1.00 | 1,108 B | - |
| MemoryPackSerializeTolerantClass | 416.28 ns | 5.47 | 1,164 B | - |
| NinoDeserializeTolerantClasses | 32.16 μs | 1.00 | - | 253624 B |
| MemoryPackDeserializeTolerantClasses | 54.74 μs | 1.70 | - | 253624 B |
| NinoSerializeTolerantClasses | 8.66 μs | 1.00 | 110,804 B | - |
| MemoryPackSerializeTolerantClasses | 39.95 μs | 4.61 | 116,404 B | - |
BclTypesTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeImmutableDictionary | 21.07 μs | 1.00 | - | 68088 B |
| MemoryPackDeserializeImmutableDictionary | 116.47 μs | 5.53 | - | 56096 B |
| NinoSerializeImmutableDictionary | 3.34 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeImmutableDictionary | 26.99 μs | 8.08 | 8,004 B | - |
| NinoDeserializeImmutableHashSet | 20.72 μs | 1.00 | - | 64096 B |
| MemoryPackDeserializeImmutableHashSet | 106.28 μs | 5.13 | - | 56104 B |
| NinoSerializeImmutableHashSet | 3.10 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableHashSet | 23.94 μs | 7.73 | 4,004 B | - |
| NinoDeserializeImmutableQueue | 7.57 μs | 1.00 | - | 36064 B |
| MemoryPackDeserializeImmutableQueue | 9.44 μs | 1.25 | - | 32112 B |
| NinoSerializeImmutableQueue | 1.98 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableQueue | 9.44 μs | 4.76 | 4,004 B | - |
| NinoDeserializeImmutableSortedDictionary | 13.58 μs | 1.00 | - | 56072 B |
| MemoryPackDeserializeImmutableSortedDictionary | 108.26 μs | 7.98 | - | - |
| NinoSerializeImmutableSortedDictionary | 3.11 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeImmutableSortedDictionary | 9.53 μs | 3.06 | 8,004 B | - |
| NinoDeserializeImmutableSortedSet | 13.00 μs | 1.00 | - | 52056 B |
| MemoryPackDeserializeImmutableSortedSet | 87.60 μs | 6.74 | - | 48088 B |
| NinoSerializeImmutableSortedSet | 2.80 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableSortedSet | 18.33 μs | 6.55 | 4,004 B | - |
| NinoDeserializeImmutableStack | 7.61 μs | 1.00 | - | 36024 B |
| MemoryPackDeserializeImmutableStack | 10.12 μs | 1.33 | - | 32040 B |
| NinoSerializeImmutableStack | 2.89 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeImmutableStack | 17.14 μs | 5.93 | 4,004 B | 4112 B |
| NinoDeserializeBigInteger | 17.31 ns | 1.00 | - | 256 B |
| NinoSerializeBigInteger | 8.79 ns | 1.01 | 203 B | - |
| NinoDeserializeBitArray | 21.44 ns | 1.00 | - | 336 B |
| NinoSerializeBitArray | 12.69 ns | 1.00 | 129 B | - |
| NinoDeserializeBlockingCollection | 5.69 μs | 1.00 | - | 12792 B |
| MemoryPackDeserializeBlockingCollection | 33.62 μs | 5.90 | - | - |
| NinoSerializeBlockingCollection | 2.89 μs | 1.00 | 4,004 B | 4096 B |
| MemoryPackSerializeBlockingCollection | 6.04 μs | 2.09 | 4,004 B | 72 B |
| NinoDeserializeCultureInfo | 29.60 ns | 1.00 | - | 64 B |
| MemoryPackDeserializeCultureInfo | 36.04 ns | 1.22 | - | 64 B |
| NinoSerializeCultureInfo | 3.10 ns | 1.01 | 9 B | - |
| MemoryPackSerializeCultureInfo | 14.34 ns | 4.64 | 13 B | - |
| NinoDeserializeLazy | 11.12 ns | 1.01 | - | 112 B |
| MemoryPackDeserializeLazy | 17.65 ns | 1.60 | - | 112 B |
| NinoSerializeLazy | 4.41 ns | 1.01 | 27 B | - |
| MemoryPackSerializeLazy | 24.05 ns | 5.51 | 31 B | - |
| NinoDeserializeLookup | 320.29 ns | 1.00 | - | 5152 B |
| MemoryPackDeserializeLookup | 392.88 ns | 1.23 | - | 5576 B |
| NinoSerializeLookup | 1.35 μs | 1.00 | 4,084 B | 1127 B |
| MemoryPackSerializeLookup | 4.98 μs | 3.70 | 4,094 B | 320 B |
| NinoDeserializeMemory | 124.25 ns | 1.00 | - | 4056 B |
| MemoryPackDeserializeMemory | 149.88 ns | 1.21 | - | 4056 B |
| NinoSerializeMemory | 21.98 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeMemory | 39.04 ns | 1.78 | 4,004 B | - |
| NinoDeserializeReadOnlyCollection | 250.64 ns | 1.00 | - | 4048 B |
| MemoryPackDeserializeReadOnlyCollection | 310.88 ns | 1.24 | - | 4048 B |
| NinoSerializeReadOnlyCollection | 2.35 μs | 1.00 | 4,004 B | 40 B |
| MemoryPackSerializeReadOnlyCollection | 17.07 μs | 7.27 | 4,004 B | 40 B |
| NinoDeserializeReadOnlySequence | 256.59 ns | 1.00 | - | 4064 B |
| MemoryPackDeserializeReadOnlySequence | 319.35 ns | 1.25 | - | 4064 B |
| NinoSerializeReadOnlySequence | 56.07 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeReadOnlySequence | 74.27 ns | 1.33 | 4,004 B | - |
| NinoDeserializeStringBuilder | 142.99 ns | 1.00 | - | 2072 B |
| MemoryPackDeserializeStringBuilder | 158.26 ns | 1.11 | - | 2072 B |
| NinoSerializeStringBuilder | 31.24 ns | 1.00 | 2,004 B | - |
| MemoryPackSerializeStringBuilder | 52.58 ns | 1.68 | 2,004 B | - |
| NinoDeserializeTimeZoneInfo | 13.74 ns | 1.00 | - | 32 B |
| MemoryPackDeserializeTimeZoneInfo | 362.35 ns | 26.37 | - | 680 B |
| NinoSerializeTimeZoneInfo | 12.00 ns | 1.00 | 11 B | - |
| MemoryPackSerializeTimeZoneInfo | 279.10 ns | 23.25 | 102 B | 216 B |
| NinoDeserializeUri | 108.22 ns | 1.00 | - | 184 B |
| MemoryPackDeserializeUri | 123.05 ns | 1.14 | - | 184 B |
| NinoSerializeUri | 10.46 ns | 1.00 | 56 B | - |
| MemoryPackSerializeUri | 29.00 ns | 2.77 | 60 B | - |
| NinoDeserializeVersion | 7.07 ns | 1.00 | - | 32 B |
| MemoryPackDeserializeVersion | 18.44 ns | 2.61 | - | 32 B |
| NinoSerializeVersion | 5.11 ns | 1.00 | 16 B | - |
| MemoryPackSerializeVersion | 22.81 ns | 4.46 | 17 B | - |
PolymorphicTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializePolymorphicClassArray | 201.60 μs | 1.00 | - | 745624 B |
| MemoryPackDeserializePolymorphicClassArray | 392.17 μs | 1.95 | - | 745624 B |
| NinoSerializePolymorphicClassArray | 58.77 μs | 1.00 | 397,304 B | - |
| MemoryPackSerializePolymorphicClassArray | 365.45 μs | 6.22 | 384,404 B | - |
| NinoDeserializePolymorphicClass | 17.85 μs | 1.00 | - | 79688 B |
| MemoryPackDeserializePolymorphicClass | 30.57 μs | 1.71 | - | 79688 B |
| NinoSerializePolymorphicClass | 6.30 μs | 1.00 | 53,789 B | - |
| MemoryPackSerializePolymorphicClass | 43.02 μs | 6.83 | 51,760 B | - |
| NinoDeserializePolymorphicFooArray | 15.47 μs | 1.00 | - | 76024 B |
| MemoryPackDeserializePolymorphicFooArray | 28.26 μs | 1.83 | - | 76024 B |
| NinoSerializePolymorphicFooArray | 6.86 μs | 1.00 | 52,404 B | - |
| MemoryPackSerializePolymorphicFooArray | 39.53 μs | 5.76 | 50,404 B | - |
PooledGraphTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoLeavesNew | 1.61 μs | 1.00 | - | 13656 B |
| NinoLeavesPooledElements | 1.36 μs | 0.85 | - | 773 B |
| NinoLeavesInPlace | 257.19 ns | 0.16 | - | - |
| MemoryPackLeavesNew | 1.89 μs | 1.18 | - | 13656 B |
| MemoryPackLeavesOverwrite | 2.24 μs | 1.39 | - | 12800 B |
| NinoNodeNew | 240.41 ns | 1.00 | - | 2192 B |
| NinoNodePooled | 70.31 ns | 0.29 | - | - |
| MemoryPackNodeNew | 319.21 ns | 1.33 | - | 2192 B |
| MemoryPackNodeOverwrite | 259.42 ns | 1.08 | - | 1024 B |
| NinoPlainNew | 1.49 μs | 1.00 | - | 12856 B |
| MemoryPackPlainNew | 1.81 μs | 1.21 | - | 12856 B |
CollectionsTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeConcurrentDictionary | 15.01 μs | 1.00 | - | 49184 B |
| MemoryPackDeserializeConcurrentDictionary | 56.44 μs | 3.76 | - | 141032 B |
| NinoSerializeConcurrentDictionary | 2.39 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeConcurrentDictionary | 3.85 μs | 1.62 | 8,004 B | - |
| NinoDeserializeHashSet | 2.58 μs | 1.00 | - | 17768 B |
| MemoryPackDeserializeHashSet | 6.56 μs | 2.54 | - | 17768 B |
| NinoSerializeHashSet | 325.03 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeHashSet | 8.46 μs | 26.01 | 4,004 B | - |
| NinoDeserializeKeyValuePairs | 423.64 ns | 1.00 | - | 8024 B |
| MemoryPackDeserializeKeyValuePairs | 458.82 ns | 1.08 | - | 8024 B |
| NinoSerializeKeyValuePairs | 88.87 ns | 1.00 | 8,004 B | - |
| MemoryPackSerializeKeyValuePairs | 95.84 ns | 1.08 | 8,004 B | - |
| NinoDeserializeLinkedList | 7.70 μs | 1.00 | - | 48040 B |
| MemoryPackDeserializeLinkedList | 10.02 μs | 1.30 | - | 48040 B |
| NinoSerializeLinkedList | 1.01 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeLinkedList | 4.69 μs | 4.62 | 4,004 B | - |
| NinoDeserializeQueue | 135.96 ns | 1.00 | - | 4064 B |
| MemoryPackDeserializeQueue | 2.11 μs | 15.51 | - | 4064 B |
| NinoSerializeQueue | 26.29 ns | 1.00 | 4,004 B | - |
| MemoryPackSerializeQueue | 4.56 μs | 173.44 | 4,004 B | - |
| NinoDeserializeSortedDictionary | 9.03 μs | 1.00 | - | 48112 B |
| MemoryPackDeserializeSortedDictionary | 27.54 μs | 3.05 | - | - |
| NinoSerializeSortedDictionary | 2.22 μs | 1.00 | 8,004 B | - |
| MemoryPackSerializeSortedDictionary | 3.78 μs | 1.71 | 8,004 B | 200 B |
| NinoDeserializeSortedList | 1.08 μs | 1.00 | - | 8112 B |
| MemoryPackDeserializeSortedList | 8.97 μs | 8.30 | - | 8112 B |
| NinoSerializeSortedList | 966.50 ns | 1.00 | 8,004 B | - |
| MemoryPackSerializeSortedList | 1.98 μs | 2.05 | 8,004 B | - |
| NinoDeserializeSortedSet | 8.21 μs | 1.00 | - | 40048 B |
| MemoryPackDeserializeSortedSet | 26.11 μs | 3.18 | - | 40048 B |
| NinoSerializeSortedSet | 1.50 μs | 1.00 | 4,004 B | - |
| MemoryPackSerializeSortedSet | 6.62 μs | 4.42 | 4,004 B | 200 B |
| NinoDeserializeTupleList | 501.60 ns | 1.00 | - | 16056 B |
| MemoryPackDeserializeTupleList | 526.35 ns | 1.05 | - | 16056 B |
| NinoSerializeTupleList | 78.61 ns | 1.02 | 12,004 B | - |
| MemoryPackSerializeTupleList | 82.18 ns | 1.07 | 16,004 B | - |
ReferenceTest
| Method | Mean | Ratio | Payload | Allocated |
|---|---|---|---|---|
| NinoDeserializeGraph | 3.46 μs | 1.00 | - | 14104 B |
| MemoryPackDeserializeGraph | 11.73 μs | 3.39 | - | 14104 B |
| NinoSerializeGraph | 3.00 μs | 1.00 | 5,878 B | - |
| MemoryPackSerializeGraph | 17.61 μs | 5.87 | 5,584 B | - |
The classes ran in parallel, each on its own runner (a class's Nino and MemoryPack cases on one):
- PooledTest, SimpleTest: INTEL XEON PLATINUM 8573C 3.50GHz, 1 CPU, 4 logical and 2 physical cores
- CommonTypesTest, FixedSizeTest: AMD EPYC 9V74 2.87GHz, 1 CPU, 4 logical and 2 physical cores
- GenericPolymorphicTest, VersionTolerantTest: AMD EPYC 9V74 3.69GHz, 1 CPU, 4 logical and 2 physical cores
- BclTypesTest, PolymorphicTest: AMD EPYC 7763 2.69GHz, 1 CPU, 4 logical and 2 physical cores
- BclTypesTest, PooledGraphTest: AMD EPYC 9V45 4.53GHz, 1 CPU, 4 logical and 2 physical cores
- BclTypesTest, CollectionsTest: AMD EPYC 7763 2.66GHz, 1 CPU, 4 logical and 2 physical cores
- CollectionsTest, ReferenceTest: AMD EPYC 9V45 4.20GHz, 1 CPU, 4 logical and 2 physical cores
Median, Min, Max and the GC counts: benchmark-x64-full.md.
Benchmark generated automatically on 2026-10-23 10:43:06 UTC