perf: don't use tracing overeager during dev (#17183)

* perf: don't use tracing overeager during dev

#17176 is a case where many sources are created and then written to (due to Svelte 4 prop mechanics), and our tracing kicked in eagerly. That combined with the excessive depth of the related stack traces slowed things down tremendously.

The fix is simple: Don't record stack traces until we've seen this source get updated for a couple of times. Additionally we now delete the `updates` map after a flush. Previously it was just an ever-growing stack trace map.

* fix

* fix
pull/16743/merge
Simon H 20 hours ago committed by GitHub
parent 3b4b0adcd5
commit ebb97a618c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
perf: don't use tracing overeager during dev

@ -53,6 +53,7 @@ export interface Analysis {
/** @deprecated use `runes` from `state.js` instead */ /** @deprecated use `runes` from `state.js` instead */
runes: boolean; runes: boolean;
immutable: boolean; immutable: boolean;
/** True if `$inspect.trace` is used */
tracing: boolean; tracing: boolean;
comments: AST.JSComment[]; comments: AST.JSComment[];

@ -57,8 +57,10 @@ function log_entry(signal, entry) {
if (dirty && signal.updated) { if (dirty && signal.updated) {
for (const updated of signal.updated.values()) { for (const updated of signal.updated.values()) {
// eslint-disable-next-line no-console if (updated.error) {
console.log(updated.error); // eslint-disable-next-line no-console
console.log(updated.error);
}
} }
} }

@ -608,6 +608,8 @@ function flush_effects() {
var was_updating_effect = is_updating_effect; var was_updating_effect = is_updating_effect;
is_flushing = true; is_flushing = true;
var source_stacks = DEV ? new Set() : null;
try { try {
var flush_count = 0; var flush_count = 0;
set_is_updating_effect(true); set_is_updating_effect(true);
@ -633,8 +635,10 @@ function flush_effects() {
} }
for (const update of updates.values()) { for (const update of updates.values()) {
// eslint-disable-next-line no-console if (update.error) {
console.error(update.error); // eslint-disable-next-line no-console
console.error(update.error);
}
} }
} }
@ -643,12 +647,24 @@ function flush_effects() {
batch.process(queued_root_effects); batch.process(queued_root_effects);
old_values.clear(); old_values.clear();
if (DEV) {
for (const source of batch.current.keys()) {
/** @type {Set<Source>} */ (source_stacks).add(source);
}
}
} }
} finally { } finally {
is_flushing = false; is_flushing = false;
set_is_updating_effect(was_updating_effect); set_is_updating_effect(was_updating_effect);
last_scheduled_effect = null; last_scheduled_effect = null;
if (DEV) {
for (const source of /** @type {Set<Source>} */ (source_stacks)) {
source.updated = null;
}
}
} }
} }

@ -188,18 +188,26 @@ export function internal_set(source, value) {
if (DEV) { if (DEV) {
if (tracing_mode_flag || active_effect !== null) { if (tracing_mode_flag || active_effect !== null) {
const error = get_stack('updated at'); source.updated ??= new Map();
if (error !== null) { // For performance reasons, when not using $inspect.trace, we only start collecting stack traces
source.updated ??= new Map(); // after the same source has been updated more than 5 times in the same flush cycle.
let entry = source.updated.get(error.stack); const count = (source.updated.get('')?.count ?? 0) + 1;
source.updated.set('', { error: /** @type {any} */ (null), count });
if (!entry) { if (tracing_mode_flag || count > 5) {
entry = { error, count: 0 }; const error = get_stack('updated at');
source.updated.set(error.stack, entry);
} if (error !== null) {
let entry = source.updated.get(error.stack);
entry.count++; if (!entry) {
entry = { error, count: 0 };
source.updated.set(error.stack, entry);
}
entry.count++;
}
} }
} }

@ -1,5 +1,8 @@
/** True if experimental.async=true */
export let async_mode_flag = false; export let async_mode_flag = false;
/** True if we're not certain that we only have Svelte 5 code in the compilation */
export let legacy_mode_flag = false; export let legacy_mode_flag = false;
/** True if $inspect.trace is used */
export let tracing_mode_flag = false; export let tracing_mode_flag = false;
export function enable_async_mode_flag() { export function enable_async_mode_flag() {

Loading…
Cancel
Save