Skip to content

Commit de6d449

Browse files
christianwell3kh0
andauthored
perf: reduce renderer startup overhead (#180)
Co-authored-by: Echo <github@3kh0.net>
1 parent 712ec68 commit de6d449

4 files changed

Lines changed: 83 additions & 21 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ build/
55
dist/
66
node_modules/
77
byoe/node_modules/
8+
byoe/slick-linux/
89
themes/.active
910
.env

plugins/Snappy/renderer.js

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'.p-message_input__input_container_unstyled [contenteditable="true"]';
1313
const originals = new WeakMap();
1414
const changed = new Set();
15+
let unsubscribe = null;
1516

1617
function disabled() {
1718
return window.__slickPluginSettings?.Snappy?.disableSpellcheck === true;
@@ -47,21 +48,28 @@
4748
}
4849
}
4950

50-
function apply(root) {
51+
function apply(root = document) {
52+
for (const editor of editors(root)) disable(editor);
53+
}
54+
55+
function sync() {
5156
if (disabled()) {
52-
for (const editor of editors(root)) disable(editor);
57+
if (!unsubscribe) {
58+
unsubscribe = window.__slickDOM.onRootsSync((added) => {
59+
prune();
60+
for (const node of added) apply(node);
61+
});
62+
}
63+
apply();
5364
return;
5465
}
66+
67+
unsubscribe?.();
68+
unsubscribe = null;
5569
for (const editor of changed) restore(editor);
5670
}
5771

58-
window.__slickDOM.onRootsSync((added) => {
59-
if (!disabled()) return;
60-
prune();
61-
for (const node of added) apply(node);
62-
});
63-
64-
const state = (window.__slickSnappy = { apply: () => apply(document) });
72+
const state = (window.__slickSnappy = { apply: sync });
6573
window.addEventListener('slick:plugin-settings', state.apply);
6674
state.apply();
6775
})();

scripts/byoe/dom-hub.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,17 @@
126126
observer.observe(document.documentElement, opts);
127127
}
128128

129+
function stopIfIdle() {
130+
if (rootSubs.length || tickSubs.length || syncSubs.length || attrSubs.length) return;
131+
observer?.disconnect();
132+
clearTimeout(timer);
133+
timer = 0;
134+
addedRoots.clear();
135+
charRoots.clear();
136+
attrHits = [];
137+
sawChildList = sawCharData = false;
138+
}
139+
129140
window.__slickDOM = {
130141
onRoots(fn, opts) {
131142
rootSubs.push({ fn, charData: !!(opts && opts.charData) });
@@ -140,6 +151,14 @@
140151
onRootsSync(fn) {
141152
syncSubs.push(fn);
142153
observe();
154+
let active = true;
155+
return () => {
156+
if (!active) return;
157+
active = false;
158+
const index = syncSubs.indexOf(fn);
159+
if (index !== -1) syncSubs.splice(index, 1);
160+
stopIfIdle();
161+
};
143162
},
144163
onAttr(fn, filter) {
145164
attrSubs.push({ fn, filter: new Set(filter) });

scripts/byoe/inject.js

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ function trackNet(sess) {
137137
if (nt) netInflight.set(d.id, { url: d.url, type: d.resourceType, start: performance.now() });
138138
});
139139
const done = (d, how) => {
140+
if (!nt) return;
140141
const e = netInflight.get(d.id);
141142
if (!e) return;
142143
netInflight.delete(d.id);
@@ -421,23 +422,28 @@ const BOOT_PROBE_JS = `(() => {
421422
if (window.__slickBootProbe) return;
422423
const p = (window.__slickBootProbe = { longtasks: 0, longtaskMs: 0, maxLongtask: 0, sw: [] });
423424
try {
424-
new PerformanceObserver((list) => {
425+
p.longtaskObserver = new PerformanceObserver((list) => {
425426
for (const e of list.getEntries()) {
426427
p.longtasks++;
427428
p.longtaskMs += e.duration;
428429
p.maxLongtask = Math.max(p.maxLongtask, e.duration);
429430
}
430-
}).observe({ type: 'longtask', buffered: true });
431+
});
432+
p.longtaskObserver.observe({ type: 'longtask', buffered: true });
431433
} catch (e) {}
432434
try {
433435
const sw = navigator.serviceWorker;
434436
if (sw) {
435437
p.sw.push('start:' + (sw.controller ? sw.controller.state : 'none'));
436-
sw.addEventListener('controllerchange', () =>
437-
p.sw.push('change@' + Math.round(performance.now()) + 'ms:' + (sw.controller ? sw.controller.state : 'none')),
438-
);
438+
p.onControllerChange = () =>
439+
p.sw.push('change@' + Math.round(performance.now()) + 'ms:' + (sw.controller ? sw.controller.state : 'none'));
440+
sw.addEventListener('controllerchange', p.onControllerChange);
439441
}
440442
} catch (e) {}
443+
p.stop = () => {
444+
p.longtaskObserver?.disconnect();
445+
if (p.onControllerChange) navigator.serviceWorker?.removeEventListener('controllerchange', p.onControllerChange);
446+
};
441447
})()`;
442448

443449
const WORKSPACE_READY_JS = `(() => {
@@ -462,6 +468,7 @@ const WORKSPACE_READY_JS = `(() => {
462468
const topHosts = Object.entries(hostMs).sort((a, b) => b[1] - a[1]).slice(0, 6).map(([h, ms]) => ({ host: h, ms: Math.round(ms) }));
463469
const p = window.__slickBootProbe || {};
464470
const sw = navigator.serviceWorker;
471+
p.stop?.();
465472
return {
466473
readyMs: Math.round(performance.now()),
467474
dclMs: nav.domContentLoadedEventEnd ? Math.round(nav.domContentLoadedEventEnd) : 0,
@@ -480,13 +487,25 @@ const WORKSPACE_READY_JS = `(() => {
480487
};
481488
return new Promise((resolve) => {
482489
if (document.querySelector(SEL)) return resolve(result());
483-
const mo = new MutationObserver(() => {
484-
if (!document.querySelector(SEL)) return;
490+
const containsWorkspace = (node) =>
491+
node.nodeType === Node.ELEMENT_NODE && (node.matches(SEL) || node.closest(SEL) || node.querySelector(SEL));
492+
let timeout;
493+
const finish = () => {
485494
mo.disconnect();
495+
clearTimeout(timeout);
486496
resolve(result());
497+
};
498+
const mo = new MutationObserver((records) => {
499+
for (const record of records) {
500+
for (const node of record.addedNodes) {
501+
if (!containsWorkspace(node)) continue;
502+
finish();
503+
return;
504+
}
505+
}
487506
});
488507
mo.observe(document.documentElement, { childList: true, subtree: true });
489-
setTimeout(() => { mo.disconnect(); resolve(result()); }, 120000);
508+
timeout = setTimeout(finish, 120000);
490509
});
491510
})()`;
492511

@@ -508,11 +527,18 @@ function formatBootDiag(r) {
508527
}
509528

510529
const consoleBuf = [];
530+
const consoleContents = new Set();
511531
function captureConsole(e, level, message) {
512532
const msg = message ?? e?.message;
513533
if (workspaceReady || consoleBuf.length >= 1200 || msg == null) return;
514534
consoleBuf.push({ t: Math.round(performance.now()), lvl: level ?? e?.level, msg: String(msg).slice(0, 240) });
515535
}
536+
function stopConsoleCapture() {
537+
for (const wc of consoleContents) {
538+
if (!wc.isDestroyed()) wc.removeListener('console-message', captureConsole);
539+
}
540+
consoleContents.clear();
541+
}
516542
function dumpConsole(reason) {
517543
if (!consoleBuf.length) return;
518544
const tail = consoleBuf.slice(-200);
@@ -537,6 +563,7 @@ function watchWorkspaceReady(wc) {
537563
bootLog(netSummary());
538564
if (r.readyMs > 5000) dumpConsole(`slow boot, workspace ${r.readyMs}ms`);
539565
nt = false;
566+
stopConsoleCapture();
540567
netInflight.clear();
541568
consoleBuf.length = 0;
542569
})
@@ -546,6 +573,7 @@ function watchWorkspaceReady(wc) {
546573
let bootReloads = 0;
547574
let stallTimer = null;
548575
const hungRenderers = new WeakSet();
576+
const BOOT_STALL_MS = 30000;
549577
function armStallWatchdog(wc) {
550578
if (workspaceReady) return;
551579
const u = URL.parse(wc.getURL());
@@ -564,13 +592,13 @@ function armStallWatchdog(wc) {
564592
}
565593
bootReloads++;
566594
bootLog(
567-
`boot-stall watchdog: workspace not ready 10000ms after dom-ready -> reload ${bootReloads}/${2} (url ${wc.getURL()})`,
595+
`boot-stall watchdog: workspace not ready ${BOOT_STALL_MS}ms after dom-ready -> reload ${bootReloads}/${2} (url ${wc.getURL()})`,
568596
);
569597
dumpConsole(`boot stall, reload ${bootReloads}`);
570598
try {
571599
wc.reload();
572600
} catch (e) {}
573-
}, 10000);
601+
}, BOOT_STALL_MS);
574602
}
575603

576604
const live = new Map();
@@ -684,7 +712,10 @@ app.on('browser-window-created', (_event, win) => {
684712
armBlocking(wc.session);
685713
ap(wc.session);
686714
setImmediate(() => ap(wc.session));
687-
wc.on('console-message', captureConsole);
715+
if (!workspaceReady) {
716+
wc.on('console-message', captureConsole);
717+
consoleContents.add(wc);
718+
}
688719
let unresponsiveAt = 0;
689720
wc.on('unresponsive', () => {
690721
unresponsiveAt = performance.now();
@@ -738,7 +769,10 @@ app.on('browser-window-created', (_event, win) => {
738769
armStallWatchdog(wc);
739770
}
740771
});
741-
wc.on('destroyed', () => live.delete(wc));
772+
wc.on('destroyed', () => {
773+
live.delete(wc);
774+
consoleContents.delete(wc);
775+
});
742776
});
743777

744778
function applyAllLive(options) {

0 commit comments

Comments
 (0)