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.