Skip to content

perf: skip metric name normalization for symbol inputs - #1

Open
artl93 wants to merge 1 commit into
mainfrom
artl93/perf
Open

perf: skip metric name normalization for symbol inputs#1
artl93 wants to merge 1 commit into
mainfrom
artl93/perf

Conversation

@artl93

@artl93 artl93 commented Feb 12, 2026

Copy link
Copy Markdown
Owner

Purpose

FromMetric currently scans and replaces every supported metric-prefix name on every call—even when the input is already a plain number or uses a metric symbol such as M. That unnecessary normalization creates several intermediate strings and dominates these common parsing paths.

This focused change avoids name normalization when the trimmed input already ends in a numeric value or a recognized standalone symbol. Name-based inputs still use the existing replacement path, including ambiguous cases such as mega versus the a symbol.

Changes

  • Detect numeric and standalone-symbol suffixes before running metric-prefix name replacement.
  • Preserve prefix-name parsing and distinguish names from one-character symbols.
  • Reuse interned one-character strings during name replacement and symbol formatting.
  • Add regression coverage for mega, attached a, and spaced a inputs.

The earlier 158-file sealing, LINQ, title-casing, and devirtualization proposal was removed. The PR now changes only MetricNumeralExtensions and its tests.

Benchmark methodology

Clean main (f9292aa90948de0aea2d4fa7d6549b1b2432c0fb) was compared with this commit (fdf80d51ee78d32f0e1a0430c72f687076c94537) using a temporary cross-build BenchmarkDotNet harness. It deliberately does not use the repository benchmark project's global InProcessEmitToolchain configuration.

  • BenchmarkDotNet 0.15.8 with the default out-of-process toolchain
  • .NET SDK 11.0.100-preview.3.26207.106; .NET 10.0.8 runtime
  • Apple M5 Pro, macOS Tahoe 26.5.2, arm64 RyuJIT
  • MemoryDiagnoser
  • Separate clean-main DLL and candidate project jobs
  • 3 process launches, 8 warmup iterations, and 20 measurement iterations
  • Fixed 1,048,576 invocations and unroll factor 16 for symmetric conditions
  • End-to-end public API calls covering symbol, spaced-symbol, prefix-name, plain-number, integer, and fractional inputs

The relevant job configuration was:

var commonJob = Job.Default
    .WithLaunchCount(3)
    .WithWarmupCount(8)
    .WithIterationCount(20)
    .WithInvocationCount(1_048_576)
    .WithUnrollFactor(16);

Command:

dotnet run -c Release --no-build -- --filter "*MetricNumeralBenchmarks*" --artifacts ../final-metric-fixed

Results

Public API scenario Clean main mean ± error Candidate mean ± error Ratio Clean alloc Candidate alloc
"123.45M".FromMetric() 163.34 ± 1.67 ns 64.50 ± 0.30 ns 0.40 464 B 80 B
"123.45 M".FromMetric() 171.05 ± 0.98 ns 72.08 ± 0.38 ns 0.42 504 B 120 B
"123.45 mega".FromMetric() 188.93 ± 0.39 ns 167.27 ± 1.22 ns 0.89 544 B 160 B
"123.45".FromMetric() 146.80 ± 0.44 ns 45.37 ± 1.01 ns 0.31 384 B 0 B
integer ToMetric with symbol 67.36 ± 0.29 ns 66.67 ± 0.36 ns 0.99 256 B 232 B
fractional ToMetric with symbol 93.61 ± 1.20 ns 90.48 ± 0.75 ns 0.97 144 B 120 B
integer ToMetric with name 67.58 ± 0.16 ns 67.21 ± 0.32 ns 0.99 240 B 240 B

The common symbol and plain-number FromMetric paths improve by 58–69% while eliminating 384 B per call. Name parsing improves by 11% and also removes 384 B. ToMetric cases remain neutral to slightly faster. No unrelated ratios are averaged, and no measured case regressed.

Disassembly is not relevant to the retained mechanism: the improvement comes from skipping normalization work and allocations, not from devirtualization or inlining.

Validation

  • Metric numeral tests: 212 passed on net10.0
  • Humanizer.Tests: 56,562 passed on net8.0
  • Humanizer.Tests: 56,562 passed on net10.0
  • Humanizer.Tests: 56,562 passed on net11.0
  • Analyzer-disabled Release pack succeeded and produced the Humanizer package
  • Changed files pass targeted dotnet format --verify-no-changes

The exact documented pack command without RunAnalyzers=false is currently blocked by pre-existing IDE0055 errors in unchanged clean-main files EnumDehumanizeExtensions.cs and PolyfillShims.cs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: 2e2e074c-44dc-42cd-978f-31553d7876dd
@artl93 artl93 changed the title perf: seal leaf classes, remove LINQ from hot paths, eliminate allocations perf: avoid metric prefix normalization for symbol inputs Jul 17, 2026
@artl93 artl93 changed the title perf: avoid metric prefix normalization for symbol inputs perf: skip metric name normalization for symbol inputs Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant