perf(i18n): skip a whole mutation inside a skipped surface, not each node

xterm's DOM renderer replaces terminal rows every frame (the split pane's
Pane B, every tile of the grid), and the translator's MutationObserver
walked each added row and ran closest(SKIP_SELECTOR) for every text node
and element in it, only to find each one inside .xterm and skip it. A CPU
profile of six printing tiles put about 2.2 s of 40 s there (closest,
translateNode, tree walks).

Now one shouldSkip(mutation.target) per record decides it: every node a
record adds or edits sits under that target, so both translators would
return on their own closest() check anyway, and the output is identical.

Measured in headless Chromium, six tiles printing 20 lines/s each, three
interleaved 40 s pairs at the same machine load: frames over 20 ms fell
from 9.1/11.5/11.8% to 7.3/8.1/8.0%; nine tiles 9.5% to 7.2%. Pinned in
i18n-branding.test: a burst of terminal rows causes no tree walk, terminal
text stays untranslated, application DOM beside it still translates.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Codeman maintainer
2026-10-06 21:29:36 +02:00
parent 0d1b91188d
commit 21beacf700
2 changed files with 48 additions and 0 deletions
+7
View File
@@ -1128,6 +1128,13 @@
observer = new MutationObserver((mutations) => {
if (applying) return;
for (const mutation of mutations) {
// A change inside a skipped surface cannot need translating: every
// node it adds or edits sits under the same skip ancestor, so both
// translators would return on their own closest() check anyway. One
// check per record instead of one per text node and attribute matters
// for xterm's DOM renderer, which replaces rows every frame (the split
// pane, every tile of the grid).
if (shouldSkip(mutation.target)) continue;
if (mutation.type === 'characterData') translateNode(mutation.target);
if (mutation.type === 'attributes') translateAttributes(mutation.target);
for (const added of mutation.addedNodes) translateNode(added);