-
-
Notifications
You must be signed in to change notification settings - Fork 47
perf(core): cache proxyIterate proxies to eliminate per-call allocations #39
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
Open
senrecep
wants to merge
8
commits into
dashersw:main
Choose a base branch
from
senrecep:feat/proxy-iterate-cache
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 2 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
c1826e2
perf(core): cache proxyIterate proxies to eliminate per-call allocations
senrecep beafef1
bench(core): add proxyIterate cache allocation benchmark
senrecep 5aaebde
bench(core): improve sort permutation benchmark with statistical rigor
senrecep 38d64a6
bench(core): rewrite getter memoization benchmark with cold/warm/mixe…
senrecep 78d0ba6
bench(vite-plugin): improve clone optimization benchmark with static …
senrecep a484f96
bench(core): rewrite proxyIterate cache benchmark with before/after c…
senrecep 13f38cd
bench(core): fix proxyIterate benchmark to exercise iterateProxyCache…
senrecep aa52b8f
fix(vite-plugin): use numeric overhead comparison and add missing cle…
senrecep File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| "@geajs/core": patch | ||
| --- | ||
|
|
||
| ### @geajs/core (patch) | ||
|
|
||
| - **proxyIterate O(1) proxy cache**: Reactive array iteration methods (`.map()`, `.filter()`, `.forEach()`, `.find()`, `.reduce()`) now reuse cached Proxy instances for object elements via a per-store `iterateProxyCache` keyed on `(array, index)`. The cache is validated by object identity and invalidated on any mutation (splice, push, set, delete, length). Reduces GC pressure in list-heavy applications. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| /** | ||
| * Benchmark: proxyIterate cache — eliminate per-call proxy allocations | ||
| * PR #39: Cache proxies in iterateProxyCache WeakMap instead of creating new ones each iteration | ||
| * | ||
| * Run: npx tsx --conditions source packages/gea/benchmarks/proxy-iterate.bench.ts | ||
| */ | ||
| import { Store } from '../src/lib/store.ts' | ||
|
|
||
| function heapMB() { | ||
| return process.memoryUsage().heapUsed / 1024 / 1024 | ||
| } | ||
|
|
||
| function bench(fn: () => void, iters: number): number { | ||
| for (let i = 0; i < 10; i++) fn() | ||
| const t0 = performance.now() | ||
| for (let i = 0; i < iters; i++) fn() | ||
| return performance.now() - t0 | ||
| } | ||
|
|
||
| class TestStore extends Store { | ||
| items = Array.from({ length: 500 }, (_, i) => ({ id: i, value: `item-${i}` })) | ||
| } | ||
|
|
||
| const ITERS = 5000 | ||
|
|
||
| console.log('\n=== proxyIterate cache benchmark ===') | ||
| console.log('Simulating repeated array iteration (index access) on a 500-item store array\n') | ||
|
|
||
| const store = new TestStore() | ||
|
|
||
| if (typeof global.gc === 'function') global.gc() | ||
| const h0 = heapMB() | ||
|
|
||
| const coldMs = bench(() => { | ||
| for (let i = 0; i < store.items.length; i++) { | ||
| void store.items[i] | ||
| } | ||
| }, 1) | ||
|
|
||
| if (typeof global.gc === 'function') global.gc() | ||
| const h1 = heapMB() | ||
|
|
||
| const warmMs = bench(() => { | ||
| for (let i = 0; i < store.items.length; i++) { | ||
| void store.items[i] | ||
| } | ||
| }, ITERS) | ||
|
|
||
| if (typeof global.gc === 'function') global.gc() | ||
| const h2 = heapMB() | ||
|
|
||
| console.log(`${'Metric'.padEnd(32)} ${'Result'.padStart(14)}`) | ||
| console.log('-'.repeat(48)) | ||
| console.log(`${'Array size'.padEnd(32)} ${'500 items'.padStart(14)}`) | ||
| console.log(`${'Iterations'.padEnd(32)} ${String(ITERS).padStart(14)}`) | ||
| console.log(`${'Cold pass (ms)'.padEnd(32)} ${coldMs.toFixed(2).padStart(14)}`) | ||
| console.log(`${'Warm ${ITERS} iters (ms)'.padEnd(32)} ${warmMs.toFixed(2).padStart(14)}`) | ||
|
coderabbitai[bot] marked this conversation as resolved.
Outdated
|
||
| console.log(`${'Per-iter (µs)'.padEnd(32)} ${((warmMs / ITERS) * 1000).toFixed(1).padStart(14)}`) | ||
| console.log(`${'Heap before warm (MB)'.padEnd(32)} ${h1.toFixed(2).padStart(14)}`) | ||
| console.log(`${'Heap after warm (MB)'.padEnd(32)} ${h2.toFixed(2).padStart(14)}`) | ||
| console.log(`${'Heap delta (MB)'.padEnd(32)} ${(h2 - h1).toFixed(3).padStart(14)}`) | ||
| console.log() | ||
| console.log('Without cache: every array[i] access allocates a new Proxy object.') | ||
| console.log('With cache: proxy reused from iterateProxyCache WeakMap → zero allocation on hit.') | ||
| console.log('Expected heap delta ≈ 0 MB with cache (proxies reused, not collected).\n') | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.