Skip to content

Commit

Permalink
refactor: minor queue perf improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
DylanPiercey committed Mar 3, 2025
1 parent cfca41a commit 4c87458
Show file tree
Hide file tree
Showing 5 changed files with 33 additions and 42 deletions.
5 changes: 5 additions & 0 deletions .changeset/ten-turtles-end.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@marko/runtime-tags": patch
---

Minor queue perf improvements.
2 changes: 1 addition & 1 deletion .sizes/counter.csr/entry.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// size: 189 (min) 144 (brotli)
// size: 189 (min) 143 (brotli)
const _clickCount_effect = effect("a0", (_scope, { 2: clickCount }) =>
on(_scope[0], "click", function () {
_clickCount(_scope, clickCount + 1);
Expand Down
14 changes: 8 additions & 6 deletions packages/runtime-tags/src/dom/controllable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,12 +171,14 @@ export function controllable_select_value(
scope[nodeAccessor + AccessorChar.ControlledType] = ControlledType.None;
}

pendingEffects.unshift(scope, () =>
setSelectOptions(
scope[nodeAccessor] as HTMLSelectElement,
value,
valueChange,
),
pendingEffects.unshift(
() =>
setSelectOptions(
scope[nodeAccessor] as HTMLSelectElement,
value,
valueChange,
),
scope,
);
}
export function controllable_select_value_effect(
Expand Down
52 changes: 18 additions & 34 deletions packages/runtime-tags/src/dom/queue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,20 @@ import type { Scope } from "../common/types";
import { finishPendingScopes } from "./scope";
import type { Signal } from "./signals";

const enum PendingEffectOffset {
Scope = 0,
Function = 1,
Total = 2,
}

type ExecFn<S extends Scope = Scope> = (scope: S, arg?: any) => void;
type PendingRender = {
___key: number;
___scope: Scope;
___signal: Signal<any>;
___value: unknown;
___key: number;
};

let pendingRenders: PendingRender[] = [];
let pendingRendersLookup = new Map<number, PendingRender>();
export let pendingEffects: unknown[] = [];
export let rendering = false;

export const scopeKeyOffset = 1024;

const scopeKeyOffset = 1e3;
export function queueRender<T>(
scope: Scope,
signal: Signal<T>,
Expand All @@ -31,29 +24,26 @@ export function queueRender<T>(
scopeKey = scope.___id,
) {
const key = scopeKey * scopeKeyOffset + signalKey;
const existingRender = signalKey !== -1 && pendingRendersLookup.get(key);

const existingRender = signalKey >= 0 && pendingRendersLookup.get(key);
if (existingRender) {
existingRender.___value = value;
} else {
let i = pendingRenders.length;
const render: PendingRender = {
___key: key,
___scope: scope,
___signal: signal,
___value: value,
___key: key,
};

pendingRendersLookup.set(key, render);
pendingRenders.push(render);
let i = pendingRenders.push(render) - 1;
while (i) {
const parentIndex = (i - 1) >> 1;
const parent = pendingRenders[parentIndex];
if (comparePendingRenders(render, parent) >= 0) break;
if (key - parent.___key >= 0) break;
pendingRenders[i] = parent;
i = parentIndex;
}

signalKey >= 0 && pendingRendersLookup.set(key, render);
pendingRenders[i] = render;
}
}
Expand All @@ -62,7 +52,7 @@ export function queueEffect<S extends Scope, T extends ExecFn<S>>(
scope: S,
fn: T,
) {
pendingEffects.push(scope, fn);
pendingEffects.push(fn, scope);
}

export function run() {
Expand Down Expand Up @@ -101,38 +91,36 @@ export function prepareEffects(fn: () => void): unknown[] {
}

export function runEffects(effects: unknown[]) {
for (let i = 0; i < effects.length; i += PendingEffectOffset.Total) {
const scope = effects[i] as Scope;
const fn = effects[i + 1] as (a: Scope, b: Scope) => void;
fn(scope, scope);
for (let i = 0, scope: Scope; i < effects.length; ) {
(effects[i++] as (a: Scope, b: Scope) => void)(
(scope = effects[i++] as Scope),
scope,
);
}
}

function runRenders() {
while (pendingRenders.length) {
const render = pendingRenders[0];
const next = pendingRenders.pop()!;
const item = pendingRenders.pop()!;

if (render !== next) {
if (render !== item) {
let i = 0;
const mid = pendingRenders.length >> 1;
const item = (pendingRenders[0] = next);
const key = (pendingRenders[0] = item).___key;

while (i < mid) {
let bestChild = (i << 1) + 1;
const right = bestChild + 1;

if (
right < pendingRenders.length &&
comparePendingRenders(
pendingRenders[right],
pendingRenders[bestChild],
) < 0
pendingRenders[right].___key - pendingRenders[bestChild].___key < 0
) {
bestChild = right;
}

if (comparePendingRenders(pendingRenders[bestChild], item) >= 0) {
if (pendingRenders[bestChild].___key - key >= 0) {
break;
} else {
pendingRenders[i] = pendingRenders[bestChild];
Expand All @@ -150,7 +138,3 @@ function runRenders() {

finishPendingScopes();
}

function comparePendingRenders(a: PendingRender, b: PendingRender) {
return a.___key - b.___key;
}
2 changes: 1 addition & 1 deletion packages/runtime-tags/src/dom/scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type { BranchScope, Scope } from "../common/types";
import { insertChildNodes, removeChildNodes } from "./dom";

let pendingScopes: Scope[] = [];
let nextID = 2 ** 32;
let nextID = 1e6;

export function createScope(
$global: Scope["$global"],
Expand Down

0 comments on commit 4c87458

Please sign in to comment.