Skip to content

Utilities: single-lookup htmlTags updates in auto-break tag stripping#12647

Merged
niksedk merged 2 commits into
SubtitleEdit:mainfrom
ivandrofly:perf/utilities-htmltags-single-lookup
Jul 19, 2026
Merged

Utilities: single-lookup htmlTags updates in auto-break tag stripping#12647
niksedk merged 2 commits into
SubtitleEdit:mainfrom
ivandrofly:perf/utilities-htmltags-single-lookup

Conversation

@ivandrofly

Copy link
Copy Markdown
Member

Summary

  • AutoBreakLineMoreThanTwoLines and AutoBreakLinePrivate strip formatting tags into an htmlTags dictionary using ContainsKey + get + set — up to three hash lookups per tag, inside per-character scanning loops that run for every auto-broken line.
  • Consolidates the three duplicated sites into an AddOrAppendHtmlTag helper that uses CollectionsMarshal.GetValueRefOrAddDefault on .NET 8+ — a single lookup for both the add and the append case (null + tag covers a newly added key) — with a TryGetValue fallback for netstandard2.1.
  • No behavior change — same tags collected at the same indexes.

Test plan

  • dotnet build src/libse/LibSE.csproj succeeds for both target frameworks (netstandard2.1 + net10.0)
  • dotnet test tests/libse/LibSETests.csproj — all 574 tests pass (auto-break is covered by the existing suite)
  • Auto-break lines containing <i>/<font> and {\an8}-style tags and confirm tags are re-inserted at the same positions as before

🤖 Generated with Claude Code

AutoBreakLineMoreThanTwoLines and AutoBreakLinePrivate collected stripped
tags with ContainsKey + get + set - up to three hash lookups per tag inside
per-character scanning loops. Route all three sites through an
AddOrAppendHtmlTag helper using CollectionsMarshal.GetValueRefOrAddDefault
on .NET 8+ (one lookup; null + tag handles the add case), with a
TryGetValue fallback for netstandard2.1.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@niksedk

niksedk commented Jul 19, 2026

Copy link
Copy Markdown
Member

Please add BenchmarkDotNet numbers for performance PRs

Benchmarking the auto-break tag-stripping loop showed no measurable gain
from the GetValueRefOrAddDefault path (tag-heavy line 3031ns -> 3074ns,
html 1182ns -> 1116ns, plain 392ns -> 388ns, identical allocations).
The loop is dominated by the s.Remove() per tag, which reallocates and
copies the whole string, so the dictionary update is rounding error.

Keep the helper - it still removes three duplicated blocks - but with a
single TryGetValue body instead of two framework-conditional paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@niksedk

niksedk commented Jul 19, 2026

Copy link
Copy Markdown
Member

Benchmarked this one with BenchmarkDotNet (net10.0, i7-1065G7) against a verbatim copy of the AutoBreakLinePrivate tag-stripping loop:

Kind Old With CollectionsMarshal
TagHeavy 3031 ns 3074 ns (1.01 — slower)
Ass 1213 ns 1183 ns (0.98)
Html 1182 ns 1116 ns (0.94)
Plain 392.5 ns 388.1 ns (0.99)
Allocated identical in every case

No real win, and the case it most targets is marginally slower. The loop is dominated by s = s.Remove(six, tag.Length), which allocates a fresh string and copies it per tag (18.6 KB for one tag-heavy line) — the dictionary update is rounding error next to that.

So I pushed b8c1c81 keeping your AddOrAppendHtmlTag helper (deduplicating the three sites is a genuine readability win) but with a single TryGetValue body instead of two framework-conditional paths — no reason to maintain both for 0%.

For context, #12645 and #12646 did measure real gains (10-11% and 9-12% respectively) and are merged as-is. If you want a bigger win in this area, the s.Remove() allocation is the thing to go after.

@niksedk
niksedk merged commit 7f54fbd into SubtitleEdit:main Jul 19, 2026
2 checks passed
@ivandrofly
ivandrofly deleted the perf/utilities-htmltags-single-lookup branch July 19, 2026 18:30
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.

2 participants