Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance: O(n^2) time-complexity with remove_reactions #14875

Open
AlbertMarashi opened this issue Jan 2, 2025 · 0 comments
Open

Performance: O(n^2) time-complexity with remove_reactions #14875

AlbertMarashi opened this issue Jan 2, 2025 · 0 comments
Labels

Comments

@AlbertMarashi
Copy link

AlbertMarashi commented Jan 2, 2025

Describe the bug

I'm building a sort of very custom use-case AI dataset editor which involves rendering approximately ~80k DOM nodes.

Occasionally, a full re-render is required, which leads to remove_reactions getting called for each "cell" in my grid I am rendering.

remove_reactions calls remove_reaction n times, which itself, performs an indexOf on the dependencies, leading to terribly slow re-renders.

image

Code responsible:

/**
* @template V
* @param {Reaction} signal
* @param {Value<V>} dependency
* @returns {void}
*/
function remove_reaction(signal, dependency) {
let reactions = dependency.reactions;
if (reactions !== null) {
var index = reactions.indexOf(signal);
if (index !== -1) {
var new_length = reactions.length - 1;
if (new_length === 0) {
reactions = dependency.reactions = null;
} else {
// Swap with last element and then remove.
reactions[index] = reactions[new_length];
reactions.pop();
}
}
}
// If the derived has no reactions, then we can disconnect it from the graph,
// allowing it to either reconnect in the future, or be GC'd by the VM.
if (
reactions === null &&
(dependency.f & DERIVED) !== 0 &&
// Destroying a child effect while updating a parent effect can cause a dependency to appear
// to be unused, when in fact it is used by the currently-updating parent. Checking `new_deps`
// allows us to skip the expensive work of disconnecting and immediately reconnecting it
(new_deps === null || !new_deps.includes(dependency))
) {
set_signal_status(dependency, MAYBE_DIRTY);
// If we are working with a derived that is owned by an effect, then mark it as being
// disconnected.
if ((dependency.f & (UNOWNED | DISCONNECTED)) === 0) {
dependency.f ^= DISCONNECTED;
}
remove_reactions(/** @type {Derived} **/ (dependency), 0);
}
}
/**
* @param {Reaction} signal
* @param {number} start_index
* @returns {void}
*/
export function remove_reactions(signal, start_index) {
var dependencies = signal.deps;
if (dependencies === null) return;
for (var i = start_index; i < dependencies.length; i++) {
remove_reaction(signal, dependencies[i]);
}
}

Reproduction

...

Logs

No response

System Info

System:
    OS: macOS 15.1
    CPU: (8) arm64 Apple M1 Pro
    Memory: 116.11 MB / 16.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 20.17.0 - ~/.nvm/versions/node/v20.17.0/bin/node
    npm: 10.8.2 - ~/.nvm/versions/node/v20.17.0/bin/npm
    pnpm: 9.7.0 - ~/Library/pnpm/pnpm
    bun: 1.1.39 - ~/.bun/bin/bun
  Browsers:
    Chrome: 131.0.6778.205
    Safari: 18.1
  npmPackages:
    svelte: ^5.16.0 => 5.16.0

Severity

annoyance

@dummdidumm dummdidumm added the perf label Jan 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants