[performance] perf(harfbuzz): avoid defensive Feature[] copy in Font.Shape - #4690
Draft
github-actions[bot] wants to merge 1 commit into
Draft
[performance] perf(harfbuzz): avoid defensive Feature[] copy in Font.Shape#4690github-actions[bot] wants to merge 1 commit into
github-actions[bot] wants to merge 1 commit into
Conversation
Font.Shape called features?.ToArray() unconditionally, allocating a fresh Feature[] copy on every shape call even on the common params Feature[] path where the caller already owns an array. hb_shape_full only reads the features (const hb_feature_t*), so pinning the caller's array directly is safe and produces identical shaping. Benchmark (net10.0, x64, Linux, FeatureCount=1): 610.3 ns/72 B -> 575.8 ns/0 B (~5.7% faster, per-call allocation eliminated). Equivalence test proves glyph infos/positions are bit-identical for array vs non-array feature lists. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
HarfBuzzSharp.Font.Shape(Buffer, IReadOnlyList<Feature>, IReadOnlyList<string>)calledfeatures?.ToArray()unconditionally, allocating a freshFeature[]copy on every shape call — even on the commonShape(Buffer, params Feature[])path where the caller already owns a real array.hb_shape_fullonly reads the features (const hb_feature_t*), so the copy is pure managed waste.This change pins the caller's array directly when it already is a
Feature[], falling back toToArray()only for non-array lists:The pinned pointer and count handed to native are identical either way, so shaping is unchanged.
Changes
binding/HarfBuzzSharp/Font.cs— pin caller-ownedFeature[]instead of copying.benchmarks/SkiaSharp.Benchmarks/Benchmarks/HarfBuzzShapeFeaturesBenchmark.cs— New-vs-Old proof.tests/Tests/HarfBuzzSharp/HBFontShapeFeaturesTest.cs— equivalence + sensitivity coverage.Required skia PR
None.
Areas Affected
Testing
Proof 1 — BenchmarkDotNet (New vs Old, InProcessEmit, net10.0, x64, Linux):
~5.7% faster; per-call
Feature[]allocation eliminated (72 B → 0 B). No allocation regression. Empirically measured on the run's hardware.Proof 2 — equivalence test
HBFontShapeFeaturesTest(5 tests, all pass): array vs non-array feature lists produce bit-identicalGlyphInfos/GlyphPositionsacross empty / 1 / 3 features and the null-list case; a guard confirms the comparison catches a genuinely different result.Built
binding/HarfBuzzSharp(net10.0) and ran the equivalence tests viatests/SkiaSharp.Tests.Console— all green.ABI
Body-only change to an existing method — ABI-safe. Renders identically before and after.
Produced by the
performance-fixeragentic workflow +performance-fixerskill (managed-C# hot-path scan, text-and-fonts focus area). Measured numbers are empirical (net10.0/x64/Linux); ns figures vary by runner, the 72 B → 0 B allocation delta is the stable primary signal.Fixes #4689