Skip to content

[performance] Remove per-call char boxing in SKFourByteTag/Tag ToString - #4752

Draft
github-actions[bot] wants to merge 1 commit into
mainfrom
dev/perf-fourbytetag-tostring-2276849d3fc5a4d6
Draft

[performance] Remove per-call char boxing in SKFourByteTag/Tag ToString#4752
github-actions[bot] wants to merge 1 commit into
mainfrom
dev/perf-fourbytetag-tostring-2276849d3fc5a4d6

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Changes

SKFourByteTag.ToString() and the general path of HarfBuzzSharp.Tag.ToString() formatted their 4-character tag with string.Concat((char),(char),(char),(char)). There is no 4-char string.Concat overload, so the call bound to string.Concat(object, object, object, object) and boxed each char — four throwaway allocations per call on top of the result string.

Both now write the four chars into a stackalloc buffer and materialize the string once via new string(char*, 0, 4). This constructor exists on every TFM (net10.0 / netstandard2.0 / net4x), so there is no #if. Body-only change — no public signature changed (ABI-safe).

Invariant that keeps it correct: each output char is exactly (char)(byte)(value >> shift) in the same big-endian order and the string is exactly 4 chars long — identical to the original Concat. The HarfBuzz None/Max/MaxSigned named special-cases are untouched and still take their early-return path.

Required skia PR

None.

Areas Affected

  • SkiaSharp (SKFourByteTag)
  • HarfBuzzSharp (Tag)

Proof — faster

In-process New-vs-Old on net10.0 / Linux x64, batch of 12 OpenType tags:

Metric Old (string.Concat) New (stackalloc)
Time ~76 ns/op ~13 ns/op (~6x)
Allocated 280 bytes/op 32 bytes/op

Runs: 5.91x / 6.17x / 5.84x; allocations 280->32 bytes/op (the 4 char boxes removed). A committed BenchmarkDotNet harness reproduces this:

dotnet run -c Release --project benchmarks/SkiaSharp.Benchmarks -- --filter '*SKFourByteTagToString*'

Proof — identical

New ToString equivalence tests compare the optimized output against the verbatim original string.Concat oracle:

  • tests/Tests/SkiaSharp/SKFourByteTagTest.cs — happy path, space-padding ("a ", "ab "), NUL, control chars (\t\r\n), >0xFF low-byte truncation, all-0xFF; plus a deliberately-wrong-result guard.
  • tests/Tests/HarfBuzzSharp/HBTagTest.cs — same coverage, plus the None/Max/MaxSigned named special-cases must still round-trip, plus a wrong-result guard.

Cross-TFM compilation verified on net10.0, netstandard2.0, and net462.

Testing

  • Standalone parity + allocation harness (bit-identical output; 280->32 bytes/op).
  • Equivalence tests added (above). The full dotnet test gate and BenchmarkDotNet harness could not run in this sandbox (network/execution restrictions blocked externals-download); the change is pure managed char/int math with no P/Invoke, so native binaries are not required to measure or validate it. Please run the full suite in CI.

Produced by the performance-fixer agentic workflow + performance-fixer skill. Empirically measured: time + allocations; statically reasoned: cross-runtime bit-exactness. No ABI impact (method bodies only). Label perf/allocations reflects the dominant measured driver (removed char boxing).

Fixes #4751

Generated by Fixer - Performance · opus48 · 472.9 AIC · ⌖ 36.8 AIC · ⊞ 13K ·

string.Concat(char,char,char,char) binds to Concat(object,...) and boxes
every char (4 allocations/call). Write into a stackalloc buffer and
materialize once via new string(char*,0,4) instead: ~6x faster, 280->32
bytes/op, bit-identical output on all TFMs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions github-actions Bot added partner/agentic-workflows Issues and PRs created by SkiaSharp agentic workflows. perf/allocations Excessive managed allocations or per-frame GC/heap churn. Implies tenet/performance. tenet/performance Performance related issues labels Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

partner/agentic-workflows Issues and PRs created by SkiaSharp agentic workflows. perf/allocations Excessive managed allocations or per-frame GC/heap churn. Implies tenet/performance. tenet/performance Performance related issues

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[performance] SKFourByteTag/Tag.ToString() boxes four chars per call via string.Concat

0 participants