Skip to content

perf(native): give memory back when idle and cut the profiler journal's footprint - #89

Draft
rubnogueira wants to merge 2 commits into
remorses:mainfrom
rubnogueira:perf/idle-shrink-and-lazy-journal
Draft

rubnogueira wants to merge 2 commits into
remorses:mainfrom
rubnogueira:perf/idle-shrink-and-lazy-journal

Conversation

@rubnogueira

Copy link
Copy Markdown
Contributor

Summary

An app that sits idle on a long thread kept memory it no longer needed. collectGarbageWhenIdle() ran a full collection, but under Bun that leaves the freed pages and the caches the engine rebuilds on demand in place. The profiler's foreground journal also held a 4 MiB ring resident from startup, for apps that never read it.

In t3's GPUI app on a long chat thread, resident memory went from about 414 MB to about 368 MB with both changes.

Changes

  • collectGarbageWhenIdle() shrinks under Bun. After the full collection the default collector calls Bun.shrink(), which hands the freed pages and rebuildable engine caches back. That is about 45 MB on a long thread. Without Bun.shrink it collects as before, and the Node --expose-gc path is unchanged. The README paragraph and the unreleased changeset describe the new behaviour.
  • Pin zed to the lazily resident profiler journal. The ring is now allocated zeroed, so it is resident only as far as entries are recorded. Nothing outside the host's hang detection reads it, so an app that does not use that holds about 4 MB less. The code change is in perf(gpui): keep the profiler journal ring out of memory until it is written zed#11. This PR moves the zed pin to its head (74bdb01) and adds a patch changeset for @gpuix/native.

Merge order

Merge remorses/zed#11 first. The zed pin points at its head, which only exists on that PR's branch until it lands.

Test plan

  • vitest run js/__tests__/idle-gc.test.ts in packages/native: 6 pass, 2 of them new (shrink is called after the forced collection, and is skipped when Bun.shrink is absent).
  • tsc --noEmit in packages/native: clean.
  • cargo check --features test-support in packages/native, on this branch with zed at 74bdb01: succeeds, with only the existing dead-code warnings.
  • bun run build in packages/native (release addon, ESM and CJS loaders, tsc): succeeds, and the new addon loads under Bun.
  • vitest run in packages/react: 536 pass.
  • vitest run js/__tests__ in packages/native: 16 pass, 1 fails. package.test.ts › bundles both public adapters for the browser needs packages/native/wasm/, which bun run build:web generates and which was not built here.
  • The journal tests (23, behind --features profiler,test-support) run in the zed PR.
  • Not run: CI, Linux, Windows.

Written with Claude Sonnet 5.5 in Cursor.

A full collection alone leaves the freed pages and the caches the engine rebuilds
on demand where they were, about 45 MB on a long chat thread. After the
collection the default collector now calls `Bun.shrink()`, which hands them back.
Node's `--expose-gc` path is unchanged.
The 4 MiB foreground profiler journal ring is allocated zeroed, so it is resident
only as far as entries are recorded. Nothing outside the host's hang detection
reads it, so an app that does not use that holds about 4 MB less.
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