Skip to main content

Performance

Benchmark results

Benchmarks compare ZeroAlloc.ValueObjects against CSharpFunctionalExtensions.ValueObject, C# record, and record struct using a two-property type (decimal Amount, string Currency). Run with BenchmarkDotNet and [MemoryDiagnoser].

MethodMeanAllocated
CFE_Equals45.2 ns96 B
Record_Equals3.1 ns0 B
RecordStruct_Equals2.8 ns0 B
ZeroAlloc_Equals3.1 ns0 B
ZeroAllocStruct_Equals2.8 ns0 B
CFE_GetHashCode38.7 ns88 B
Record_GetHashCode2.4 ns0 B
RecordStruct_GetHashCode2.2 ns0 B
ZeroAlloc_GetHashCode2.4 ns0 B
ZeroAllocStruct_GetHashCode2.2 ns0 B

ZeroAlloc.ValueObjects matches record and record struct exactly. The CFE baseline allocates ~90 bytes per equality call.

Why CFE allocates

// Each call allocates: iterator state machine + boxed value types
protected override IEnumerable<IComparable> GetEqualityComponents()
{
yield return Amount; // decimal → boxed object (heap)
yield return Currency;
}
// Iterator object itself also allocated on heap

Why ZeroAlloc is zero-allocation

// Generated — no heap objects created
public bool Equals(Money? other) =>
other is not null &&
Amount == other.Amount && // direct decimal comparison
Currency == other.Currency; // direct string comparison

public override int GetHashCode() =>
System.HashCode.Combine(Amount, Currency); // stack-only

Running benchmarks

dotnet run -c Release --project benchmarks/ZeroAlloc.ValueObjects.Benchmarks