Skip to content

Performance: three libse micro-optimizations (verified with BenchmarkDotNet)#12575

Merged
niksedk merged 1 commit into
mainfrom
perf/libse-micro-optimizations-5
Jul 17, 2026
Merged

Performance: three libse micro-optimizations (verified with BenchmarkDotNet)#12575
niksedk merged 1 commit into
mainfrom
perf/libse-micro-optimizations-5

Conversation

@niksedk

@niksedk niksedk commented Jul 17, 2026

Copy link
Copy Markdown
Member

Three micro-optimizations, each verified with BenchmarkDotNet (v0.15.8, .NET 10): the "Old" column is the previous code's kernel replicated verbatim in the bench harness; "New" calls the actual patched libse code.

Benchmark (per operation) Old New Speedup Alloc old Alloc new
Language detection sweep (40+ word lists, 8 KB text) 494.5 ms 8.6 ms ~58× 1,049 KB 127 KB
ASSA resample, one tagged line (pos/org/move/clip) 20.6 µs 3.1 µs ~6.7× 42.3 KB 4.6 KB
Format lookup scan over all ~300 formats 6.5 µs 5.4 µs ~1.2× 22.2 KB 0.5 KB (−98%)

1. LanguageAutoDetect — cache compiled regexes

One detection probes ~40+ large alternation patterns (hundreds of words each) via static Regex.Matches. That exceeds the built-in regex cache (Regex.CacheSize = 15), so every AutoDetectGoogleLanguage call re-parsed every pattern — on every subtitle open, live-spell-check re-arm, and speech-to-text setup. Now cached in a ConcurrentDictionary with RegexOptions.Compiled (bounded — the word lists are fixed for the app lifetime). GetCountContains gets the same treatment.

2. AssaResampler — stop compiling regexes per line

FixMethodTwoParameters/FourParameters/SixParametersFourActive/FixTagWithNumber built new Regex("\\\\" + tag + …) per call — 6–8 regex compiles per dialogue line during resolution resampling — even though the class already had a GetCachedRegex pattern cache these four sites bypassed. They now use it. (The "New" benchmark runs the full real ResampleOverrideTagsPosition including the value rewriting the "Old" kernel skips, so the real gain is understated if anything.)

3. SubtitleFormat.FriendlyName — cache the interpolation

FriendlyName => $"{Name} ({Extension})" allocated a new string on every access, and FromName/GetSubtitleFormatByFriendlyName loop it over all ~300 formats. Now cached per instance — reference-guarded on Extension, because MPlayer2/TimedText* extensions are settings-dependent and a naive cache would go stale when those settings change (constants are interned, settings values keep their instance until changed, so the guard is exact). The win here is allocation elimination; time is dominated by the string comparisons either way.

🤖 Generated with Claude Code

…DotNet)

- LanguageAutoDetect: cache compiled regexes for the word-count
  patterns. One detection probes ~40+ large alternation patterns -
  far more than the built-in regex cache holds (Regex.CacheSize is
  15), so every AutoDetectGoogleLanguage call re-parsed every pattern
  (subtitle open, live spell check arming, speech to text setup).
  Detection sweep: 494 ms -> 8.6 ms (~58x), allocations 1049 KB ->
  127 KB.

- AssaResampler: route the per-line tag regexes through the existing
  pattern cache. The Fix*Parameters/FixTagWithNumber methods built
  6-8 fresh Regex instances per dialogue line during resampling while
  a GetCachedRegex cache already existed in the class. One tagged
  line: 20.6 us -> 3.1 us (~6.7x), allocations 42 KB -> 4.6 KB.

- SubtitleFormat.FriendlyName: cache the "$"{Name} ({Extension})""
  interpolation, which allocated on every access and is looped over
  all ~300 formats in FromName/GetSubtitleFormatByFriendlyName. The
  cache is reference-guarded on Extension because MPlayer2/TimedText
  extensions are settings-dependent. Lookup scan allocations:
  22 KB -> 0.5 KB (-98%).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@niksedk
niksedk merged commit 5fb07f8 into main Jul 17, 2026
1 check was pending
@niksedk
niksedk deleted the perf/libse-micro-optimizations-5 branch July 17, 2026 13:16
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