perf: store current_sources as Set for O(1) membership checks (#18278)

`current_sources` tracks the sources created within the active reaction
so that reading or writing them during that same reaction doesn't
trigger a re-run. It was an `Array` checked with
`Array.prototype.includes.call(...)` in three hot places: the
`state_unsafe_mutation` guard, `set()`'s destruction check, and
`schedule_possible_effect_self_invalidation`.

---------

Co-authored-by: Rich Harris <rich.harris@vercel.com>
pull/18265/merge
Mathias Picker 2 days ago committed by GitHub
parent 0bb715d3cf
commit 67090e8ee8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -0,0 +1,5 @@
---
'svelte': patch
---
perf: store `current_sources` as a `Set` for O(1) membership checks

@ -32,7 +32,6 @@ import {
} from '#client/constants';
import * as e from '../errors.js';
import { legacy_mode_flag, tracing_mode_flag } from '../../flags/index.js';
import { includes } from '../../shared/utils.js';
import { tag_proxy } from '../dev/tracing.js';
import { get_error } from '../../shared/dev.js';
import { component_context, is_runes } from '../context.js';
@ -158,7 +157,7 @@ export function set(source, value, should_proxy = false) {
(!untracking || (active_reaction.f & EAGER_EFFECT) !== 0) &&
is_runes() &&
(active_reaction.f & (DERIVED | BLOCK_EFFECT | ASYNC | EAGER_EFFECT)) !== 0 &&
(current_sources === null || !includes.call(current_sources, source))
(current_sources === null || !current_sources.has(source))
) {
e.state_unsafe_mutation();
}

@ -90,18 +90,14 @@ export function set_active_effect(effect) {
/**
* When sources are created within a reaction, reading and writing
* them within that reaction should not cause a re-run
* @type {null | Source[]}
* @type {null | Set<Source>}
*/
export let current_sources = null;
/** @param {Value} value */
export function push_reaction_value(value) {
if (active_reaction !== null && (!async_mode_flag || (active_reaction.f & DERIVED) !== 0)) {
if (current_sources === null) {
current_sources = [value];
} else {
current_sources.push(value);
}
(current_sources ??= new Set()).add(value);
}
}
@ -202,7 +198,7 @@ function schedule_possible_effect_self_invalidation(signal, effect, root = true)
var reactions = signal.reactions;
if (reactions === null) return;
if (!async_mode_flag && current_sources !== null && includes.call(current_sources, signal)) {
if (!async_mode_flag && current_sources !== null && current_sources.has(signal)) {
return;
}
@ -540,7 +536,7 @@ export function get(signal) {
// we don't add the dependency, because that would create a memory leak
var destroyed = active_effect !== null && (active_effect.f & DESTROYED) !== 0;
if (!destroyed && (current_sources === null || !includes.call(current_sources, signal))) {
if (!destroyed && (current_sources === null || !current_sources.has(signal))) {
var deps = active_reaction.deps;
if ((active_reaction.f & REACTION_IS_UPDATING) !== 0) {

Loading…
Cancel
Save