Skip to content

Reduce Preact prop updater allocations - #978

Closed
JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/preact-prop-updater-memory
Closed

Reduce Preact prop updater allocations#978
JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/preact-prop-updater-memory

Conversation

@JoviDeCroock

Copy link
Copy Markdown
Member

Each signal-bound DOM property currently allocates a wrapper object, updater closure, effect callback closure, and bound disposer. Make the Effect itself the property updater and share its callback and update methods across bindings, while retaining the wrapper Signal that preserves rebinding semantics.

Trade-off: signals.mjs grows from 1732 B to 1821 B gzip (+89 B). Each live binding is 72 B smaller, so the shipped cost breaks even after a few bound properties.

Signal-bound DOM properties — 52.5% faster creation, 72 B smaller, 11.1% faster updates
import { h, render } from "preact";
import { signal } from "@preact/signals";

const count = 10_000;
const values = Array.from({ length: count }, (_, i) => signal(`before-${i}`));
const root = document.createElement("div");
document.body.append(root);

function App() {
  return h(
    "main",
    null,
    values.map((value, i) => h("span", { title: value }, i))
  );
}
render(h(App), root);

async function write(value) {
  const start = performance.now();
  for (let i = 0; i < count; i++) values[i].value = `${value}-${i}`;
  await Promise.resolve();
  await Promise.resolve();
  await Promise.resolve();
  return performance.now() - start;
}

for (let i = 0; i < 5; i++) await write(`warm-${i}`);
const samples = [];
for (let i = 0; i < 15; i++) samples.push(await write(`sample-${i}`));
console.log(Math.min(...samples));

@changeset-bot

changeset-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: eef7237

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@preact/signals Patch
preact-signals-devtools Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploy Preview for preact-signals-demo ready!

Name Link
🔨 Latest commit eef7237
🔍 Latest deploy log https://app.netlify.com/projects/preact-signals-demo/deploys/6a9294644adcfb000871c38c
😎 Deploy Preview https://deploy-preview-978--preact-signals-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: -6.94 kB (-3.35%)

Total Size: 200 kB

📦 View Changed
Filename Size Change
docs/dist/assets/bench-********.js 1.6 kB +2 B (+0.13%)
docs/dist/assets/devtools-********.js 915 B +4 B (+0.44%)
docs/dist/assets/EmbeddedDevtools-********.js 23.1 kB +73 B (+0.32%)
docs/dist/assets/index-********.js 1.09 kB -7.42 kB (-87.15%) 🏆
docs/dist/assets/signals.module-********.js 2.74 kB +80 B (+3.01%)
docs/dist/assets/Unmount-********.js 652 B +1 B (+0.15%)
docs/dist/assets/utils.module-********.js 521 B +1 B (+0.19%)
docs/dist/basic-********.js 248 B +1 B (+0.4%)
packages/devtools-ui/dist/devtools-ui.js 21.3 kB +67 B (+0.32%)
packages/devtools-ui/dist/devtools-ui.mjs 20.7 kB +74 B (+0.36%)
packages/preact/dist/signals.js 1.91 kB +84 B (+4.61%) 🔍
packages/preact/dist/signals.mjs 1.85 kB +88 B (+5%) 🔍
ℹ️ View Unchanged
Filename Size
docs/dist/assets/client-********.js 46.6 kB
docs/dist/assets/jsxRuntime.module-********.js 300 B
docs/dist/assets/preact.module-********.js 4.74 kB
docs/dist/assets/signals-core.module-********.js 1.92 kB
docs/dist/assets/style-********.css 7.19 kB
docs/dist/nesting-********.js 1.15 kB
docs/dist/react-********.js 242 B
packages/core/dist/signals-core.js 1.94 kB
packages/core/dist/signals-core.mjs 1.93 kB
packages/debug/dist/debug.js 4.72 kB
packages/debug/dist/debug.mjs 4.24 kB
packages/devtools-adapter/dist/devtools-adapter.js 2.36 kB
packages/devtools-adapter/dist/devtools-adapter.mjs 2.07 kB
packages/preact-transform/dist/signals-transform.js 1.66 kB
packages/preact-transform/dist/signals-transform.mjs 1.61 kB
packages/preact-transform/dist/signals-transform.umd.js 1.77 kB
packages/react-transform/dist/signals-transform.js 7.63 kB
packages/react-transform/dist/signals-transform.mjs 6.78 kB
packages/react-transform/dist/signals-transform.umd.js 7.75 kB
packages/react/dist/signals.js 214 B
packages/react/dist/signals.mjs 165 B
packages/vite-plugin/dist/vite-plugin.js 8.86 kB
packages/vite-plugin/dist/vite-plugin.mjs 7.86 kB

compressed-size-action

@JoviDeCroock

Copy link
Copy Markdown
Member Author

Byte size impact is a bit too large

@JoviDeCroock
JoviDeCroock deleted the JoviDeCroock/preact-prop-updater-memory branch August 29, 2026 08:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant