From 8696fd84bfa28726e43b492d304b55dbb32134a7 Mon Sep 17 00:00:00 2001 From: Priyansh4444 Date: Thu, 10 Sep 2026 18:53:07 -0700 Subject: [PATCH] perf: pack subscription edges to remove parallel slot arrays --- .changeset/packed-subscription-edges.md | 5 ++ packages/solid/src/reactive/signal.ts | 61 ++++++++++++------------- 2 files changed, 33 insertions(+), 33 deletions(-) create mode 100644 .changeset/packed-subscription-edges.md diff --git a/.changeset/packed-subscription-edges.md b/.changeset/packed-subscription-edges.md new file mode 100644 index 000000000..204374972 --- /dev/null +++ b/.changeset/packed-subscription-edges.md @@ -0,0 +1,5 @@ +--- +"solid-js": patch +--- + +Pack subscription edges into a single array per side, removing the parallel `observerSlots`/`sourceSlots` arrays. diff --git a/packages/solid/src/reactive/signal.ts b/packages/solid/src/reactive/signal.ts index ccce84178..030a0ef60 100644 --- a/packages/solid/src/reactive/signal.ts +++ b/packages/solid/src/reactive/signal.ts @@ -84,8 +84,8 @@ export interface SourceMapValue { export interface SignalState extends SourceMapValue { value: T; - observers: Computation[] | null; - observerSlots: number[] | null; + // packed [observer, slotInObserverSources, ...] pairs + observers: (Computation | number)[] | null; tValue?: T; comparator?: (prev: T, next: T) => boolean; // development-only @@ -105,8 +105,8 @@ export interface Computation extends Owner { fn: EffectFunction; state: ComputationState; tState?: ComputationState; - sources: SignalState[] | null; - sourceSlots: number[] | null; + // packed [source, slotInSourceObservers, ...] pairs + sources: (SignalState | number)[] | null; value?: Init; updatedAt: number | null; pure: boolean; @@ -235,7 +235,6 @@ export function createSignal( const s: SignalState = { value, observers: null, - observerSlots: null, comparator: options.equals || undefined }; @@ -455,7 +454,6 @@ export function createMemo( ) as Partial>; c.observers = null; - c.observerSlots = null; c.comparator = options.equals || undefined; if (Scheduler && Transition && Transition.running) { c.tState = STALE; @@ -1155,7 +1153,6 @@ export function devComponent(Comp: (props: P) => V, props: P): V { ) as DevComponent

; c.props = props; c.observers = null; - c.observerSlots = null; c.name = Comp.name; c.component = Comp; updateComputation(c); @@ -1316,21 +1313,19 @@ export function readSignal(this: SignalState | Memo) { } if (Listener) { const observers = this.observers; - if (!observers || observers[observers.length - 1] !== Listener) { - const sSlot = observers ? observers.length : 0; - if (!Listener.sources) { - Listener.sources = [this]; - Listener.sourceSlots = [sSlot]; + if (!observers || observers[observers.length - 2] !== Listener) { + const sources = Listener.sources, + sSlot = observers ? observers.length >> 1 : 0, + cSlot = sources ? sources.length >> 1 : 0; + if (!sources) { + Listener.sources = [this, sSlot]; } else { - Listener.sources.push(this); - Listener.sourceSlots!.push(sSlot); + sources.push(this, sSlot); } if (!observers) { - this.observers = [Listener]; - this.observerSlots = [Listener.sources.length - 1]; + this.observers = [Listener, cSlot]; } else { - observers.push(Listener); - this.observerSlots!.push(Listener.sources.length - 1); + observers.push(Listener, cSlot); } } } @@ -1352,8 +1347,8 @@ export function writeSignal(node: SignalState | Memo, value: any, isCo } else node.value = value; if (node.observers && node.observers.length) { runUpdates(() => { - for (let i = 0; i < node.observers!.length; i += 1) { - const o = node.observers![i]; + for (let i = 0; i < node.observers!.length; i += 2) { + const o = node.observers![i] as Computation; const TransitionRunning = Transition && Transition.running; if (TransitionRunning && Transition!.disposed.has(o)) continue; if (TransitionRunning ? !o.tState : !o.state) { @@ -1451,7 +1446,6 @@ function createComputation( updatedAt: null, owned: null, sources: null, - sourceSlots: null, cleanups: null, value: init, owner: Owner, @@ -1671,7 +1665,7 @@ function lookUpstream(node: Computation, ignore?: Computation) { const runningTransition = Transition && Transition.running; if (runningTransition) node.tState = 0; else node.state = 0; - for (let i = 0; i < node.sources!.length; i += 1) { + for (let i = 0; i < node.sources!.length; i += 2) { const source = node.sources![i] as Memo; if (source.sources) { const state = runningTransition ? source.tState : source.state; @@ -1685,8 +1679,8 @@ function lookUpstream(node: Computation, ignore?: Computation) { function markDownstream(node: Memo) { const runningTransition = Transition && Transition.running; - for (let i = 0; i < node.observers!.length; i += 1) { - const o = node.observers![i]; + for (let i = 0; i < node.observers!.length; i += 2) { + const o = node.observers![i] as Computation; if (runningTransition ? !o.tState : !o.state) { if (runningTransition) o.tState = PENDING; else o.state = PENDING; @@ -1700,17 +1694,18 @@ function markDownstream(node: Memo) { function cleanNode(node: Owner) { let i; if ((node as Computation).sources) { - while ((node as Computation).sources!.length) { - const source = (node as Computation).sources!.pop()!, - index = (node as Computation).sourceSlots!.pop()!, + const sources = (node as Computation).sources!; + while (sources.length) { + const index = sources.pop() as number, + source = sources.pop() as SignalState, obs = source.observers; if (obs && obs.length) { - const n = obs.pop()!, - s = source.observerSlots!.pop()!; - if (index < obs.length) { - n.sourceSlots![s] = index; - obs[index] = n; - source.observerSlots![index] = s; + const s = obs.pop() as number, + n = obs.pop() as Computation; + if (index < obs.length >> 1) { + n.sources![(s << 1) + 1] = index; + obs[index << 1] = n; + obs[(index << 1) + 1] = s; } } }