Perf hunt round 19: remaining per-char Substring loops, Cavena cp1252 string cache - #14595
Merged
Conversation
…s, cache Cavena's cp1252 strings
The leftovers from round 18: ESub-XF, TextST, Ultech 130, DVD Studio Pro, Unknown 81 and
the right-to-left start/end reverser all probed tags with line.Substring(i).StartsWith(tag)
for every character (TextST also made a one-char string per character). They now use the
span-based StartsWithAt / EndsWithAt helpers.
Cavena 890: FixColors decoded eight colour markers through encoding.GetString(new byte[])
and allocated a one-char string for every character; FixText ran about 150
encoding.GetString(new byte[] { .. }) calls per paragraph for constant one- and two-byte
cp1252 strings. The markers are decoded once per call and the strings come from a static
cache. The writer's Substring(i, 3) == "<i>" probes are span compares.
FixMissingPeriodsAtEndOfLine split the whole next paragraph into words to read the first
one.
Benchmarks in tests/benchmarks/PerfHuntRound19Benchmarks.cs; outputs (including the Cavena
and Ultech bytes) are byte-identical to the baseline over an edge-case corpus.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.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.
Follow-up to #14594 with the candidates that round found but left. Nine verified fixes (BenchmarkDotNet default job, in-process, Apple M4, .NET 10) against a detached baseline worktree of
main; the untouchedSubRipControlbenchmark is flat with identical allocations. Every touched function was dumped over an edge-case corpus on both sides, including the Cavena 890 and Ultech 130 output bytes and Cavena files patched with colour-marker and raw-remap bytes, and the dumps are byte-identical.What was wrong
Substringprobes:ESubXf.GenerateLineWithSpan(eight probes per character),TextST.DialogPresentationSegment(copied the rest of the line and then a one-char string per character),Ultech130.Save,DvdStudioPro.DecodeStyles,UnknownSubtitle81.EncodeTextandUtilities.ReverseStartAndEndingForRightToLeft(three tail copies per character at the front, a prefix copy per character at the back). All use the span-basedStartsWithAt, plus a newEndsWithAt.FixColorscompared a one-char string per character against eight freshencoding.GetString(new byte[] { .. })decodes;FixTextran about 150 such decodes per paragraph for constant cp1252 strings. The markers are decoded once per call and the one- and two-byte strings come from a static cache.text.Substring(i, 3) == "<i>"per character became a span compare; the existing length guards are unchanged.Dropped during the round: caching the
new SubRip()inToLowercaseButKeepTagsmeasured no difference at all (identical allocations), so it is not included.Verification
tests/benchmarks/PerfHuntRound19Benchmarks.cstests/libse: 1916 passed🤖 Generated with Claude Code