Skip to main content

MoonBuggy Microbenchmarks

Benchmarks run with BenchmarkDotNet v0.14.0 on:

  • OS: Ubuntu 24.04.3 LTS (WSL)
  • CPU: 12th Gen Intel Core i9-12900K, 1 CPU, 24 logical / 12 physical cores
  • Runtime: .NET 8.0.23 (RyuJIT AVX2)

WriteTo Performance

Measures IHtmlContent.WriteTo() overhead for TranslatedString and TranslatedHtml against raw TextWriter.Write() baselines.

MethodMeanErrorStdDevMedianRatioRatioSDAllocatedAlloc Ratio
StringBaseline_Write299.6 ns30.34 ns81.52 ns301.0 ns1.090.47736 B1.00
HtmlStringBaseline_WriteTo466.5 ns26.96 ns71.96 ns458.5 ns1.700.62736 B1.00
TranslatedString_SingleSegment474.9 ns57.26 ns165.21 ns428.5 ns1.730.84736 B1.00
TranslatedString_MultiSegment1,208.3 ns76.71 ns223.78 ns1,137.0 ns4.401.67736 B1.00
TranslatedHtml_SingleSegment472.0 ns47.51 ns133.22 ns436.0 ns1.720.76736 B1.00
TranslatedHtml_MultiSegment497.7 ns24.24 ns67.57 ns498.0 ns1.810.6464 B0.09

Key findings:

  • Single-segment TranslatedString and TranslatedHtml match the HtmlString.WriteTo() baseline (~470 ns).
  • Multi-segment TranslatedHtml allocates only 64 B (0.09x ratio) — the pre-rendered HTML path avoids per-segment allocations.
  • All 736 B allocations come from the StringWriter infrastructure itself, not from MoonBuggy.

ToString vs WriteTo

Compares ToString() (allocates a string) against WriteTo() (writes directly to TextWriter).

MethodMeanErrorStdDevMedianRatioRatioSDAllocatedAlloc Ratio
WriteTo_SingleSegment379.88 ns22.87 ns62.22 ns370.50 ns1.030.23736 B1.00
ToString_SingleSegment36.11 ns14.12 ns41.64 ns19.50 ns0.100.11736 B1.00
WriteTo_MultiSegment1,054.43 ns67.82 ns194.58 ns987.50 ns2.850.69736 B1.00
ToString_MultiSegment853.65 ns60.38 ns166.30 ns810.00 ns2.300.57784 B1.07
ImplicitConversion876.63 ns43.93 ns119.51 ns877.50 ns2.370.49784 B1.07

Key findings:

  • Single-segment ToString() is ~10x faster than WriteTo() — it returns the pre-stored string directly without TextWriter overhead.
  • Multi-segment ToString() allocates 784 B (1.07x) due to the intermediate string concatenation.
  • Implicit string conversion matches ToString() performance as expected.

Interceptor Simulation

Simulates the code patterns emitted by the source generator interceptors, measuring the cost of locale lookup, variable substitution, and plural resolution.

MethodMeanErrorStdDevMedianAllocated
SourceLocale_Simple591.4 ns62.26 ns182.6 ns522.5 ns736 B
SourceLocale_Variable1,734.6 ns113.30 ns330.5 ns1,672.5 ns816 B
SourceLocale_Plural_One1,990.4 ns102.58 ns287.6 ns1,926.0 ns816 B
SourceLocale_Plural_Other1,959.9 ns101.23 ns290.5 ns1,868.5 ns816 B
MultiLocale_Simple1,386.1 ns90.93 ns265.3 ns1,322.0 ns736 B
MultiLocale_Variable2,587.1 ns170.23 ns491.2 ns2,437.0 ns816 B

Key findings:

  • Source-locale simple translation: ~590 ns with zero extra allocations beyond the StringWriter.
  • Variable substitution adds ~1.1 µs per call due to additional Write() calls and argument access.
  • Plural resolution adds minimal overhead over variable substitution (~250 ns).
  • Multi-locale lookup (LCID switch) adds ~800 ns over the source-locale path.
  • The 80 B difference (736 → 816 B) in variable/plural benchmarks comes from the args object allocation.

Running Benchmarks

# All benchmarks
dotnet run --project tests/MoonBuggy.Benchmarks -c Release -- --filter '*'

# Individual suites
dotnet run --project tests/MoonBuggy.Benchmarks -c Release -- --filter '*WriteTo*'
dotnet run --project tests/MoonBuggy.Benchmarks -c Release -- --filter '*ToString*'
dotnet run --project tests/MoonBuggy.Benchmarks -c Release -- --filter '*Interceptor*'

Results are saved to BenchmarkDotNet.Artifacts/ in the project root.