# Nino generates C# serializers at compile time instead of at runtime

> Nino is a binary serializer for .NET that writes one formatter per type with a source generator, so there is no reflection, no IL emit and no registration step at startup. Its readme claims speed and zero allocation gains over MemoryPack on a documented benchmark, and the current release line is still a set of 4.0.0 release candidates.

**JasonXuDeveloper/Nino** — The fastest binary serializer for C#. Source-generated, zero-allocation: services, RPC, messaging, caching, storage, Unity and Godot.

- Repository: https://github.com/JasonXuDeveloper/Nino
- Website: https://nino.xgamedev.net
- Stars: 650 · Forks: 58
- Language: C#
- License: MIT
- Published: 2026-10-21 · Updated: 2026-10-21 · Language: en
- Canonical page: https://hysenlabs.com/projects/jasonxudeveloper-nino

## One formatter per type, produced by a source generator

The mechanism behind Nino is that nothing is decided at runtime. A source generator writes each formatter, and the readme is explicit about what that removes: no reflection, no IL emit, no registration and no init call. That absence is what makes the library usable in environments where runtime code generation is unavailable or expensive, and the readme names them, IL2CPP, NativeAOT and HybridCLR hot updates. Nino.Core is annotated for trimming and Native AOT, and with the .NET 8 SDK or later, calls are intercepted to the exact formatter with no virtual call.

The other half of the design is incremental generation. The generator is incremental, with one file per type, and only the type you edit is regenerated. The readme puts a number on the effect: compared with v3, the generated code is 14 times smaller and compiles 51 times faster for 330 types and 221 times faster for 1,100 types.

The practical consequence is that the cost of Nino moves to build time. Types that Nino cannot see cannot be serialized, and a project with heavy dynamic type creation is a poor fit. Everything that is declared statically is exactly the case the library is built for.

## The speed claims come with a documented benchmark environment

The readme leads with the numbers, and they are worth reading with their environment attached rather than as bare multipliers. It claims up to 5.6 times faster serialization and up to 11.5 times faster in-place deserialization than MemoryPack, alongside a claim of 221 times faster builds than v3 and zero garbage collector allocations on hot paths.

The environment is specified in a footnote. The benchmark uses BenchmarkDotNet 0.15.8 on .NET 10.0.13 with server garbage collection on an AMD EPYC 7763 at 2.59 GHz. Nino v4 and MemoryPack 1.21.4 are compared in one run, while Nino 3.3.13 is taken from NuGet directly before it, on the same machine. Payloads are roughly the same size, 1,104 bytes in v4, 1,152 in v3 and 1,148 in MemoryPack for the SimpleClass case.

That level of detail is unusual and welcome, because it tells you how to read the numbers. The comparison is against one baseline version on one runtime on one processor, and the per-case table with the method names lives in docs/v4-benchmarks.md, with each release's notes carrying that release's figures. The readme's own more modest claim is the one to keep in view, since serialization of classes and polymorphic types is given as 3.4 to 5.6 times faster, deserialization as 1.5 to 1.9 times faster, and raw struct arrays as at least on par because both libraries do a block copy there.

## Zero allocation comes from three separate mechanisms

The allocation claim is not a single feature but three different paths, and the readme separates them.

Serializing into a buffer typed as IBufferWriter<byte> allocates nothing. That interface is what a PipeWriter or a pooled buffer implements, so the serializer writes into memory someone else owns.

Deserializing into existing or pooled objects also allocates nothing. The attribute for that is NinoPooled, and the mechanism is reuse deserialization into existing objects, which the readme says is up to 11.5 times faster than MemoryPack's overwrite mode. A string whose text is unchanged is kept rather than reallocated, which is a detail that matters in practice because string allocation is usually what pushes a deserialization path onto the garbage collector.

The third mechanism is thread safety, which is stated separately: serialize and deserialize from any number of threads, with no locks on the hot path. That is what makes the buffer and pooled object paths usable from ASP.NET Core workers, actors and background services without a lock around the shared serializer.

Note the scope. These guarantees are about the hot paths Nino controls. Values you allocate yourself around the call, and the pooled objects whose lifecycle you manage, remain your responsibility.

## Identical bytes across runtimes, architectures and engines

The interoperability claim is specific. The netstandard2.0, netstandard2.1, net8.0 and net10.0 targets write identical bytes on CoreCLR, Mono and IL2CPP, in 64-bit and 32-bit builds, on x64 and Arm64. The stated use is a Unity client and a .NET server talking to each other directly, with no transcoding layer.

That is a strong constraint to design around, and it is also a promise you have to verify rather than trust, because the readme does not describe the wire format in enough detail to confirm it by reading. The way to check it is to write a payload with one runtime and read it with another in your own test suite, which is the check most worth automating before a client and a server are deployed separately.

The package is distributed for three ecosystems rather than one, which the badges at the top of the readme reflect: a NuGet package named Nino, an OpenUPM package named com.jasonxudeveloper.nino for Unity, and the project documentation site at nino.xgamedev.net, with a Chinese readme as well. The repository itself has a benchmarks/ directory, a docs/ directory, src/ and tools/ at the top level, and separate source folders for the core library and its satellite packages.

## The type list is long and the analyzer is the safety net

Nino covers the types a .NET project already uses, and the list in the readme is worth scanning before you evaluate it. Primitives, strings, tuples, every collection in the base class library, BigInteger, Uri, Version, Memory<T>, ILookup of two parameters, Lazy<T> and more are all named, along with every immutable collection under Nino.Immutable, Unity's AnimationCurve, Gradient and RectOffset, and your own collection types. Anything else goes through a pluggable formatter, so the escape hatch exists rather than the type list needing to be complete.

The safety net for all of this is the analyzer. Nino ships 26 analyzer diagnostics, numbered NINO001 to NINO026, and the readme notes that many have a one-click fix. That matters more than the count, because a serializer that fails at compile time with a precise diagnostic is a different experience from one that throws at runtime on a field you forgot to mark.

Two related features are opt-in per type, and the readme is explicit that types which do not use them pay nothing. Version tolerance, keyed members and reference tracking for shared references and cycles are all in that group. The reference tracking is the one to examine carefully in a service that has object graphs, since cycles are exactly the case a naive serializer will fail on.

## ASP.NET Core bodies and TypeScript codecs for the same bytes

Two satellite packages extend the format beyond the .NET process. Nino.AspNetCore adds MVC formatters and Minimal API bodies and results for the content type application/x-nino, so an HTTP endpoint can accept and return Nino payloads without hand writing a converter. Nino.TypeScript generates TypeScript codecs for your types after each build, and the point of it is that browsers and Node read and write the same bytes as .NET.

The TypeScript side is the interesting one for a mixed stack, because it removes the usual fallback of a JSON boundary. The readme notes that the codecs are generated after each build, which means the types are shared with the front end rather than transcribed, and the source of truth for the format becomes the C# annotations.

The readme is also explicit about the intended domains in a table of use cases. Services and IPC, web and Node clients, RPC and networking, message queues and event streams, caching, and persistence and storage. In each case the argument is the same set of properties: compact payloads, no per-call allocations and safe concurrent use. The messaging row adds a property worth noting for anyone building a queue, formats that let producers and consumers upgrade at different times.

## Version 4 is still a release candidate series

The release history is the part to weigh before adopting. The three most recent tags are v4.0.0-rc.6 on 2026-10-19, v4.0.0-rc.5 on 2026-10-18 and v4.0.0-rc.4 on 2026-10-17, a cadence that suggests active work on the new major version. The last push was on 2026-10-20, and the repository is not archived.

What that means in practice is that v4 is a release candidate series rather than a stable major release, while v3 is the line you can depend on today, with 3.3.13 being the version used in the benchmark comparison. There is also a specific cost to a major version in a serializer: the wire format is the contract, and the readme points to a page titled what is new in v4 alongside a separate page comparing Nino against MemoryPack. A team that already has v3 data in a database or on the wire has to plan for that migration rather than assume the new major is a drop in replacement.

The licence is MIT, with the LICENSE file at the top level of the repository. The repository also carries a CLAUDE.md and a CONTRIBUTING.md, and the project publishes a DeepWiki link for its own documentation alongside the website, which is a reasonable setup for a library whose documentation has to be read carefully before use.

## Conclusion

Adopt Nino when your .NET, Unity or Godot project serializes hot path payloads and needs identical bytes across runtimes, and when you are prepared to accept a v4 release candidate rather than a stable major version. Skip it if you need a battle tested stable release today or if your types rely on runtime reflection based polymorphism that the source generator cannot see. Before you commit, read the benchmark page and docs/v4-benchmarks.md so you know the measured environment, check the v4 documentation for what changed from 3.3.13, and run the analyzer diagnostics NINO001 to NINO026 over your existing types, since mistakes are caught at compile time rather than in production.

## FAQ

### What is Nino and how does it serialize C# types?

Nino is a binary serializer for .NET that uses a source generator to write one formatter per type at build time. There is no reflection, no IL emit, no registration and no init call, and Nino.Core is annotated for trimming and Native AOT.

### How do I get Nino for .NET, Unity or Godot?

The project publishes a NuGet package named Nino and an OpenUPM package named com.jasonxudeveloper.nino for Unity, with badges for both at the top of the readme. The documentation site is nino.xgamedev.net, and the getting started page is linked from the readme.

### Is Nino faster than MemoryPack?

The readme claims it is, citing up to 5.6 times faster serialization and up to 11.5 times faster in-place deserialization, measured with BenchmarkDotNet 0.15.8 on .NET 10.0.13 with server GC on an AMD EPYC 7763. The per-case table and method names are in docs/v4-benchmarks.md.

### How does Nino avoid allocations on hot paths?

Serializing into an IBufferWriter<byte> allocates nothing, and deserializing into existing or pooled objects marked with NinoPooled allocates nothing either, with unchanged strings kept rather than reallocated. Nino also states that serialization and deserialization are thread safe with no locks on the hot path.

### What is the current release status of Nino?

The recent tags are v4.0.0-rc.6 on 2026-10-19, v4.0.0-rc.5 on 2026-10-18 and v4.0.0-rc.4 on 2026-10-17, so version 4 is still in release candidate. The benchmark comparison uses Nino 3.3.13 from NuGet as the previous version, and the last push was on 2026-10-20.

### Can Nino types be used from a browser or from Node?

Yes. Nino.TypeScript generates TypeScript codecs for your types after each build so browsers and Node read and write the same bytes as .NET, and Nino.AspNetCore adds MVC formatters and Minimal API support for the application/x-nino content type.

## Sources

- [JasonXuDeveloper/Nino on GitHub](https://github.com/JasonXuDeveloper/Nino)
- [License: MIT](https://github.com/JasonXuDeveloper/Nino/blob/main/LICENSE)
- [Project website](https://nino.xgamedev.net)
- [README](https://github.com/JasonXuDeveloper/Nino/blob/main/README.md)
- [Releases](https://github.com/JasonXuDeveloper/Nino/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jasonxudeveloper-nino
