The library is built for low allocation and high throughput:
- Identity and event types (
ReplicaId,Dot,Timestamp) arereadonly structs. - Serialization is
Span<T>/IBufferWriter<byte>based, with LEB128 varints and pooled buffers; there is no reflection on any hot path. - Hot paths avoid LINQ and unnecessary allocation.
GCounterandPNCounteradd one synchronization object and an ordering token per instance. Their existing algorithms and per-replica storage remain unchanged; reads and mutations acquire instance monitors, and binary operations lock both operands. Merges do not allocate operand snapshots. Contended operations serialize, including the positive/negative pair in a PN counter; measure throughput for your contention level.- On .NET 8+ the build uses the runtime's modern primitives (
BinaryPrimitives,MemoryMarshal,Unsafe); onnetstandard2.0andnetstandard2.1the same code paths are kept working through polyfills.
On .NET 8 and later the library is annotated IsAotCompatible, which turns on the trimming and AOT analyzers during the build — so AOT incompatibilities are caught at compile time, as build errors, rather than at publish time.
The test suite itself is published and executed as a NativeAOT binary in CI, which proves end to end that the public API — including binary and source-generated JSON serialization — runs without a JIT:
dotnet publish tests/Crdt.Tests/Crdt.Tests.csproj -c Release -f net10.0 -r <rid> -p:AotTest=true
./tests/Crdt.Tests/bin/Release/net10.0/<rid>/publish/Crdt.Testsnetstandard2.0 and netstandard2.1 are compatibility targets for older runtimes (.NET Framework 4.6.1+, Unity, Mono, .NET Core 3.x) and are not themselves NativeAOT-published; AOT applies to the net8/9/10 targets.
- Counters, registers, sets, maps, and flags are backed by hash maps; merges and lookups are near-linear in the number of entries.
- Observed-remove types keep no tombstones, so their size tracks live data plus a compact causal context rather than growing with every deletion.
Rga<T>resolves the visible sequence with a pre-order traversal; index-based insert/delete are linear in the document length. For very large documents this is the main thing to measure for your workload.
The repository includes a BenchmarkDotNet project under tests/Crdt.Benchmarks covering per-family merge, mutation, and binary/JSON serialization comparisons. Run it with:
dotnet run -c Release --project tests/Crdt.BenchmarksBecause BenchmarkDotNet spawns and instruments processes, the benchmark project runs under the JIT, not NativeAOT.