[performance] perf(SKRuntimeEffectUniforms): pre-size uniform lookup map to known count - #4512
Conversation
…ount Build the internal name->Variable dictionary with an explicit capacity (names.Length) instead of the default (zero) capacity. The constructor already knows the exact final entry count up front, so the default-sized map needlessly rehashes several times while it is filled, allocating throwaway bucket/entry arrays on a per-uniform-set (potentially per-frame) path. Behaviour is identical: dictionary capacity is a pure sizing hint. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
📖 Documentation Preview The documentation for this PR has been deployed and is available at: 🔗 View Staging Site This preview will be updated automatically when you push new commits to this PR. This comment is automatically updated by the documentation staging workflow. |
📊 SkiaSharp benchmarks — PR #4512⭐ this PR (full source build) vs 🌙 nightly · Linux · Windows · macOS
Highlights⏱️ Time — 🔴 16 slower · 🟢 42 faster
Full per-OS benchmark deltasLinux⏱️ Time (vs 🌙 nightly
Windows⏱️ Time (vs 🌙 nightly
macOS⏱️ Time (vs 🌙 nightly
|
What changed
SKRuntimeEffectUniforms's constructor now builds its internalDictionary<string, Variable>withan explicit capacity equal to the known uniform count:
The constructor already knows the final entry count (
names.Length) before the fill loop, so thedefault-sized (capacity 0) map needlessly rehashes several times as it is populated, allocating
throwaway bucket/entry arrays. This is on a per-uniform-set (per-frame for animated shaders) path.
Invariant that keeps it correct:
Dictionarycapacity is a pure sizing hint — it changes onlythe initial bucket allocation, never lookup/insert semantics or iteration results. The map still
holds exactly
names.Lengthentries.Proof it is faster
BenchmarkDotNet,
[MemoryDiagnoser], net10.0, AMD EPYC 9V74, 2 runs.Old= default-sized map,New= pre-sized; both fillNentries:Repeatable across both runs; no allocation regression (fewer allocations from removed rehashes).
Benchmark added at
benchmarks/SkiaSharp.Benchmarks/Benchmarks/SKRuntimeEffectUniformsDictionaryBenchmark.cs.Proof it is identical
New equivalence test
SKRuntimeEffectTest.Shaders.UniformsMapIsCompleteForManyUniformsbuilds a24-uniform effect (crossing several dictionary rehash boundaries) and asserts:
Count;order (proving each name maps to the correct offset regardless of insertion path);
Confirmed the test catches a deliberately-wrong result: truncating the constructor loop to
i < names.Length - 1turns it RED; the shipped fix is GREEN. Full*Uniform*suite: 81 passed /0 failed against pre-built natives (
dotnet cake --target=externals-download) on net10.0.Scope note
binding/SkiaSharp/SKRuntimeEffect.cs); no*.generated.cs, noexternals/skia.perf/allocations.Fixes #4511