test: stabilize codspeed memory benchmarks - #7988
Conversation
📝 WalkthroughWalkthroughThe PR adds isolated child-process execution for memory benchmarks. It introduces shared client and server registration helpers, migrates existing scenarios to those helpers, adds IPC and process-isolation tests, updates benchmark configuration, and documents the execution model. ChangesMemory benchmark isolation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Vitest
participant BenchmarkRegistrar
participant IsolatedMemoryProcess
participant isolated-process-child
participant Workload
Vitest->>BenchmarkRegistrar: register benchmark
BenchmarkRegistrar->>IsolatedMemoryProcess: start setup process
IsolatedMemoryProcess->>isolated-process-child: send run request
isolated-process-child->>Workload: execute workload
Workload-->>isolated-process-child: return or throw
isolated-process-child-->>IsolatedMemoryProcess: send completion or error
IsolatedMemoryProcess-->>BenchmarkRegistrar: resolve or reject benchmark
BenchmarkRegistrar-->>Vitest: finish benchmark lifecycle
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
benchmarks/memory/shared/isolated-process.ts (2)
192-196: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winAwait child exit in the failure path.
child.kill()only requests termination.start()returns before the child exits, so a failed start can leave a live process that competes for memory with the next run.#waitForExitalready handles the exit transition safely.♻️ Proposed change
} catch (error) { + const exit = this.#waitForExit(child) child.kill() this.#child = undefined + await exit.catch(() => {}) throw error }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@benchmarks/memory/shared/isolated-process.ts` around lines 192 - 196, Update the catch block in start() to await `#waitForExit` after requesting child termination, ensuring the failed child has fully exited before rethrowing the original error. Preserve the existing child reference cleanup and error propagation.
295-347: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick winConsider a timeout for IPC waits.
#waitForMessagesettles only on a message, an error, or child exit. If a workload stalls,run()andstop()never settle, and the failure surfaces later as an opaque runner timeout. A bounded wait that kills the child and rejects with the workload name would make the failure diagnosable.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@benchmarks/memory/shared/isolated-process.ts` around lines 295 - 347, Add a bounded timeout to `#waitForMessage` so stalled IPC waits terminate deterministically. On timeout, clean up listeners, kill the child process, and reject with an error that includes the workload name; also clear the timer whenever the wait settles through a message, error, or exit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@benchmarks/memory/client/isolated-benchmark.ts`:
- Around line 31-38: Update the memory benchmark suite around isolatedProcess to
remove or neutralize the describe-level beforeEach and afterEach hooks, leaving
lifecycle management exclusively to the setup and teardown callbacks in bench
options. Preserve the existing isolatedProcess.start() and
isolatedProcess.stop() calls in those Tinybench callbacks.
In `@benchmarks/memory/server/isolated-process.test.ts`:
- Around line 24-28: Update the afterEach cleanup around runner.stop() so
environment-variable deletion and temporary-directory removal always execute in
a finally block, even when stop() rejects. Also reset the runner reference
during cleanup, using the existing runner and tempDirectory symbols.
In `@benchmarks/memory/shared/isolated-process-child.ts`:
- Around line 222-250: Update the commandQueue chain around the message handler
so failures from either the main operation or the catch-block send are contained
at every link. Reuse the handler for both fulfillment and rejection, e.g. attach
it as both callbacks to commandQueue.then, and ensure the error-reporting send
cannot leave the chain rejected so later run and stop messages continue
processing.
---
Nitpick comments:
In `@benchmarks/memory/shared/isolated-process.ts`:
- Around line 192-196: Update the catch block in start() to await `#waitForExit`
after requesting child termination, ensuring the failed child has fully exited
before rethrowing the original error. Preserve the existing child reference
cleanup and error propagation.
- Around line 295-347: Add a bounded timeout to `#waitForMessage` so stalled IPC
waits terminate deterministically. On timeout, clean up listeners, kill the
child process, and reject with an error that includes the workload name; also
clear the timer whenever the wait settles through a message, error, or exit.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fc9d0b9f-7700-4377-a465-1174a1582619
📒 Files selected for processing (64)
benchmarks/memory/README.mdbenchmarks/memory/client/isolated-benchmark.tsbenchmarks/memory/client/package.jsonbenchmarks/memory/client/scenarios/interrupted-navigations/react/memory.bench.tsbenchmarks/memory/client/scenarios/interrupted-navigations/react/vite.config.tsbenchmarks/memory/client/scenarios/interrupted-navigations/solid/memory.bench.tsbenchmarks/memory/client/scenarios/interrupted-navigations/solid/vite.config.tsbenchmarks/memory/client/scenarios/interrupted-navigations/vue/memory.bench.tsbenchmarks/memory/client/scenarios/interrupted-navigations/vue/vite.config.tsbenchmarks/memory/client/scenarios/loader-data-retention/react/memory.bench.tsbenchmarks/memory/client/scenarios/loader-data-retention/react/vite.config.tsbenchmarks/memory/client/scenarios/loader-data-retention/solid/memory.bench.tsbenchmarks/memory/client/scenarios/loader-data-retention/solid/vite.config.tsbenchmarks/memory/client/scenarios/loader-data-retention/vue/memory.bench.tsbenchmarks/memory/client/scenarios/loader-data-retention/vue/vite.config.tsbenchmarks/memory/client/scenarios/mount-unmount/react/memory.bench.tsbenchmarks/memory/client/scenarios/mount-unmount/react/vite.config.tsbenchmarks/memory/client/scenarios/mount-unmount/solid/memory.bench.tsbenchmarks/memory/client/scenarios/mount-unmount/solid/vite.config.tsbenchmarks/memory/client/scenarios/mount-unmount/vue/memory.bench.tsbenchmarks/memory/client/scenarios/mount-unmount/vue/vite.config.tsbenchmarks/memory/client/scenarios/navigation-churn/react/memory.bench.tsbenchmarks/memory/client/scenarios/navigation-churn/react/vite.config.tsbenchmarks/memory/client/scenarios/navigation-churn/solid/memory.bench.tsbenchmarks/memory/client/scenarios/navigation-churn/solid/vite.config.tsbenchmarks/memory/client/scenarios/navigation-churn/vue/memory.bench.tsbenchmarks/memory/client/scenarios/navigation-churn/vue/vite.config.tsbenchmarks/memory/client/scenarios/preload-churn/react/memory.bench.tsbenchmarks/memory/client/scenarios/preload-churn/react/vite.config.tsbenchmarks/memory/client/scenarios/preload-churn/shared.tsbenchmarks/memory/client/scenarios/preload-churn/solid/memory.bench.tsbenchmarks/memory/client/scenarios/preload-churn/solid/vite.config.tsbenchmarks/memory/client/scenarios/preload-churn/vue/memory.bench.tsbenchmarks/memory/client/scenarios/preload-churn/vue/vite.config.tsbenchmarks/memory/client/scenarios/unique-location-churn/react/memory.bench.tsbenchmarks/memory/client/scenarios/unique-location-churn/react/vite.config.tsbenchmarks/memory/client/scenarios/unique-location-churn/shared.tsbenchmarks/memory/client/scenarios/unique-location-churn/solid/memory.bench.tsbenchmarks/memory/client/scenarios/unique-location-churn/solid/vite.config.tsbenchmarks/memory/client/scenarios/unique-location-churn/vue/memory.bench.tsbenchmarks/memory/client/scenarios/unique-location-churn/vue/vite.config.tsbenchmarks/memory/client/tsconfig.jsonbenchmarks/memory/server/isolated-benchmark.tsbenchmarks/memory/server/isolated-process.test.tsbenchmarks/memory/server/package.jsonbenchmarks/memory/server/scenarios/aborted-requests/react/memory.bench.tsbenchmarks/memory/server/scenarios/aborted-requests/solid/memory.bench.tsbenchmarks/memory/server/scenarios/aborted-requests/vue/memory.bench.tsbenchmarks/memory/server/scenarios/error-paths/react/memory.bench.tsbenchmarks/memory/server/scenarios/error-paths/shared.tsbenchmarks/memory/server/scenarios/error-paths/solid/memory.bench.tsbenchmarks/memory/server/scenarios/error-paths/vue/memory.bench.tsbenchmarks/memory/server/scenarios/request-churn/react/memory.bench.tsbenchmarks/memory/server/scenarios/request-churn/shared.tsbenchmarks/memory/server/scenarios/request-churn/solid/memory.bench.tsbenchmarks/memory/server/scenarios/request-churn/vue/memory.bench.tsbenchmarks/memory/server/scenarios/server-fn-churn/react/memory.bench.tsbenchmarks/memory/server/scenarios/server-fn-churn/solid/memory.bench.tsbenchmarks/memory/server/scenarios/server-fn-churn/vue/memory.bench.tsbenchmarks/memory/server/test-fixtures/isolated-process-setup.tsbenchmarks/memory/server/tsconfig.jsonbenchmarks/memory/shared/isolated-process-child.tsbenchmarks/memory/shared/isolated-process.tsbenchmarks/memory/shared/tsconfig.json
| describe('memory', () => { | ||
| beforeEach(() => isolatedProcess.start()) | ||
| afterEach(() => isolatedProcess.stop()) | ||
|
|
||
| bench(options.name, run, { | ||
| ...memoryBenchOptions, | ||
| setup: () => isolatedProcess.start(), | ||
| teardown: () => isolatedProcess.stop(), |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
For Vitest 4.1.4 benchmark files, verify whether suite-level beforeEach/afterEach hooks run around bench() tasks and whether Tinybench setup/teardown run for each benchmark cycle.
💡 Result:
In Vitest 4.1.4, the behavior of suite-level hooks and Tinybench setup/teardown in benchmark files is as follows: Suite-Level Hooks (beforeEach/afterEach) Historically, Vitest's suite-level beforeEach and afterEach hooks did not run for bench tasks [1]. While there have been community efforts and pull requests (such as PR #7541) aimed at enabling this functionality, these hooks are generally not standardly supported or guaranteed to execute around benchmark tasks in the same way they do for test blocks [1][2][3]. Benchmark execution in Vitest has historically followed a distinct lifecycle from standard tests [1][3]. Tinybench Setup/Teardown Tinybench, which powers Vitest's benchmarking, explicitly provides setup and teardown options [4][5][6]. These are configured as part of the third argument (options object) passed to the bench function [4][5]. According to Tinybench documentation, these setup and teardown functions are designed to run for each benchmark cycle (task execution) [6][7][8]. They are intended for operations that need to occur immediately before and after the specific code being measured, allowing users to handle stateful setups that should not be included in the timed performance result [3][6]. In summary, you should rely on the explicit setup and teardown options provided in the bench function's configuration object to manage cycle-specific logic, rather than relying on suite-level beforeEach/afterEach hooks [3][4][6]. Top Results: [1][2][3][4][6]
Citations:
- 1: Support hooks in
benchAPI vitest-dev/vitest#5075 - 2: feat: add hooks in bench mode vitest-dev/vitest#7541
- 3: Expose
beforeEachandafterEachhooks from tinybenchTaskvitest-dev/vitest#7599 - 4: https://vitest.dev/api/test
- 5: https://main.vitest.dev/api/test
- 6: https://tinylibs.github.io/tinybench/interfaces/BenchOptions.html
- 7: https://tinylibs.github.io/tinybench/classes/Bench.html
- 8: https://tinylibs.github.io/tinybench/interfaces/ResolvedBenchOptions.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Locate files:"
fd -a 'isolated-benchmark\.ts$|isolated.*memory.*\.ts$|memory.*\.ts$' . | sed 's#^\./##' | head -100
echo
echo "Target file outline:"
ast-grep outline benchmarks/memory/client/isolated-benchmark.ts --view compact || true
echo
echo "Target file lines:"
cat -n benchmarks/memory/client/isolated-benchmark.ts | sed -n '1,120p'
echo
echo "Search isolated process definitions/usages:"
rg -n "class IsolatedMemoryProcess|IsolatedMemoryProcess|start\(|stop\(|isolatedProcess|bench\\(" -S benchmarks . --glob '*.ts' --glob '!node_modules' | head -200Repository: TanStack/router
Length of output: 25556
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Vitest version declarations:"
for f in package.json pnpm-lock.yaml yarn.lock package-lock.json; do
if [ -f "$f" ]; then
echo "--- $f ---"
if [ "$f" = "package.json" ]; then
node -e "const p=require('./package.json'); console.log({devDependencies:p.devDependencies?.vitest, dependencies:p.dependencies?.vitest, scripts:p.scripts});" 2>/dev/null || sed -n '/vitest/p' "$f"
else
rg -n "vitest(@|:)|version:" "$f" | head -80
fi
fi
done
echo
echo "Shared process start/stop implementation:"
cat -n benchmarks/memory/shared/isolated-process.ts | sed -n '140,235p'
echo
echo "Isolated process tests around duplicate start:"
cat -n benchmarks/memory/server/isolated-process.test.ts | sed -n '1,130p'
echo
echo "Memory bench options:"
cat -n benchmarks/memory/client/bench-utils.ts | sed -n '1,120p'
echo
echo "Server isolated benchmark counterpart:"
cat -n benchmarks/memory/server/isolated-benchmark.ts | sed -n '1,70p'Repository: TanStack/router
Length of output: 10199
Use Tinybench setup/teardown for the isolated lifecycle.
In Vitest 4 benchmark mode, keep the setup and teardown in the bench() options, but remove the suite beforeEach/afterEach hooks or make them no-ops. The suite hooks can start the process before bench() setup/teardown runs, while IsolatedMemoryProcess.start() rejects a second start because a child process already exists.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@benchmarks/memory/client/isolated-benchmark.ts` around lines 31 - 38, Update
the memory benchmark suite around isolatedProcess to remove or neutralize the
describe-level beforeEach and afterEach hooks, leaving lifecycle management
exclusively to the setup and teardown callbacks in bench options. Preserve the
existing isolatedProcess.start() and isolatedProcess.stop() calls in those
Tinybench callbacks.
| afterEach(async () => { | ||
| await runner?.stop() | ||
| delete process.env.TSR_MEMORY_ISOLATION_TEST_LOG | ||
| await rm(tempDirectory, { recursive: true }) | ||
| }) |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Make cleanup independent of stop() success.
If runner.stop() rejects, afterEach skips the env-var deletion and the temp-directory removal. The stale TSR_MEMORY_ISOLATION_TEST_LOG value then leaks into later tests, and temp directories accumulate. Run the cleanup in a finally block, and reset runner.
🧹 Proposed fix
afterEach(async () => {
- await runner?.stop()
- delete process.env.TSR_MEMORY_ISOLATION_TEST_LOG
- await rm(tempDirectory, { recursive: true })
+ try {
+ await runner?.stop()
+ } finally {
+ runner = undefined
+ delete process.env.TSR_MEMORY_ISOLATION_TEST_LOG
+ await rm(tempDirectory, { recursive: true, force: true })
+ }
})📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| afterEach(async () => { | |
| await runner?.stop() | |
| delete process.env.TSR_MEMORY_ISOLATION_TEST_LOG | |
| await rm(tempDirectory, { recursive: true }) | |
| }) | |
| afterEach(async () => { | |
| try { | |
| await runner?.stop() | |
| } finally { | |
| runner = undefined | |
| delete process.env.TSR_MEMORY_ISOLATION_TEST_LOG | |
| await rm(tempDirectory, { recursive: true, force: true }) | |
| } | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@benchmarks/memory/server/isolated-process.test.ts` around lines 24 - 28,
Update the afterEach cleanup around runner.stop() so environment-variable
deletion and temporary-directory removal always execute in a finally block, even
when stop() rejects. Also reset the runner reference during cleanup, using the
existing runner and tempDirectory symbols.
| commandQueue = commandQueue.then(async () => { | ||
| try { | ||
| if (message.type === 'run') { | ||
| const workload = loaded.workloads[message.workloadIndex] | ||
|
|
||
| if (!workload) { | ||
| throw new Error( | ||
| `Invalid isolated memory workload index ${message.workloadIndex}`, | ||
| ) | ||
| } | ||
|
|
||
| await workload.run() | ||
| await settle(completionSettleTurns) | ||
| await send({ type: 'complete', requestId: message.requestId }) | ||
| return | ||
| } | ||
|
|
||
| stopping = true | ||
| await loaded.cleanup() | ||
| await send({ type: 'stopped', requestId: message.requestId }) | ||
| process.exit(0) | ||
| } catch (error) { | ||
| await send({ | ||
| type: 'error', | ||
| requestId: message.requestId, | ||
| error: serializeError(error), | ||
| }) | ||
| } | ||
| }) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
A failed send breaks the command queue permanently.
The catch block awaits send(...). If that send rejects, for example when the IPC channel is closing, the callback rejects and commandQueue becomes a rejected promise. The next commandQueue.then(cb) has no rejection handler, so cb never executes. The child then ignores all later 'run' and 'stop' messages while staying alive, and Node reports an unhandled rejection. The parent waits until exit or a runner timeout.
Terminate the rejection at each link.
🐛 Proposed fix
- commandQueue = commandQueue.then(async () => {
+ commandQueue = commandQueue.then(async () => {
try {
if (message.type === 'run') {
const workload = loaded.workloads[message.workloadIndex]
if (!workload) {
throw new Error(
`Invalid isolated memory workload index ${message.workloadIndex}`,
)
}
await workload.run()
await settle(completionSettleTurns)
await send({ type: 'complete', requestId: message.requestId })
return
}
stopping = true
await loaded.cleanup()
await send({ type: 'stopped', requestId: message.requestId })
process.exit(0)
} catch (error) {
await send({
type: 'error',
requestId: message.requestId,
error: serializeError(error),
- })
+ }).catch(() => {})
}
- })
+ })Additionally, guard the chain itself so a rejection cannot block later commands:
commandQueue = commandQueue.then(handler, handler)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| commandQueue = commandQueue.then(async () => { | |
| try { | |
| if (message.type === 'run') { | |
| const workload = loaded.workloads[message.workloadIndex] | |
| if (!workload) { | |
| throw new Error( | |
| `Invalid isolated memory workload index ${message.workloadIndex}`, | |
| ) | |
| } | |
| await workload.run() | |
| await settle(completionSettleTurns) | |
| await send({ type: 'complete', requestId: message.requestId }) | |
| return | |
| } | |
| stopping = true | |
| await loaded.cleanup() | |
| await send({ type: 'stopped', requestId: message.requestId }) | |
| process.exit(0) | |
| } catch (error) { | |
| await send({ | |
| type: 'error', | |
| requestId: message.requestId, | |
| error: serializeError(error), | |
| }) | |
| } | |
| }) | |
| commandQueue = commandQueue.then(async () => { | |
| try { | |
| if (message.type === 'run') { | |
| const workload = loaded.workloads[message.workloadIndex] | |
| if (!workload) { | |
| throw new Error( | |
| `Invalid isolated memory workload index ${message.workloadIndex}`, | |
| ) | |
| } | |
| await workload.run() | |
| await settle(completionSettleTurns) | |
| await send({ type: 'complete', requestId: message.requestId }) | |
| return | |
| } | |
| stopping = true | |
| await loaded.cleanup() | |
| await send({ type: 'stopped', requestId: message.requestId }) | |
| process.exit(0) | |
| } catch (error) { | |
| await send({ | |
| type: 'error', | |
| requestId: message.requestId, | |
| error: serializeError(error), | |
| }).catch(() => {}) | |
| } | |
| }) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@benchmarks/memory/shared/isolated-process-child.ts` around lines 222 - 250,
Update the commandQueue chain around the message handler so failures from either
the main operation or the catch-block send are contained at every link. Reuse
the handler for both fulfillment and rejection, e.g. attach it as both callbacks
to commandQueue.then, and ensure the error-reporting send cannot leave the chain
rejected so later run and stop messages continue processing.
Merging this PR will degrade performance by 49.3%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Memory | mem client interrupted-navigations (react) |
250.1 KB | 2,255 KB | -88.91% |
| ❌ | Memory | mem client loader-data-retention (react) |
155.5 KB | 1,164.2 KB | -86.64% |
| ❌ | Memory | mem client loader-data-retention (solid) |
153.4 KB | 1,079.8 KB | -85.8% |
| ❌ | Memory | mem client unique-location-churn (solid) |
342.1 KB | 2,373.7 KB | -85.59% |
| ❌ | Memory | mem client interrupted-navigations (solid) |
341.1 KB | 2,263.8 KB | -84.93% |
| ❌ | Memory | mem client preload-churn (solid) |
311.8 KB | 1,548.1 KB | -79.86% |
| ❌ | Memory | mem client navigation-churn (solid) |
538.1 KB | 2,486.5 KB | -78.36% |
| ❌ | Memory | mem client interrupted-navigations (vue) |
501.8 KB | 2,284.8 KB | -78.04% |
| ❌ | Memory | mem client navigation-churn (react) |
542.4 KB | 2,178.2 KB | -75.1% |
| ❌ | Memory | mem client unique-location-churn (react) |
762.5 KB | 2,444.6 KB | -68.81% |
| ❌ | Memory | mem server request-churn (solid) |
425.4 KB | 1,261.4 KB | -66.28% |
| ❌ | Memory | mem client mount-unmount (solid) |
475.5 KB | 1,262.4 KB | -62.33% |
| ❌ | Memory | mem server error-paths not-found (vue) |
328.8 KB | 872.2 KB | -62.3% |
| ❌ | Memory | mem client unique-location-churn (vue) |
1,003.9 KB | 2,431 KB | -58.71% |
| ❌ | Memory | mem server error-paths unmatched (vue) |
479.9 KB | 1,046.4 KB | -54.14% |
| ❌ | Memory | mem server aborted-requests (react) |
562.8 KB | 1,225.1 KB | -54.06% |
| ❌ | Memory | mem server error-paths not-found (react) |
250.7 KB | 539.2 KB | -53.5% |
| ❌ | Memory | mem client mount-unmount (vue) |
832 KB | 1,628.7 KB | -48.91% |
| ❌ | Memory | mem client navigation-churn (vue) |
1.2 MB | 2.2 MB | -45.16% |
| ❌ | Memory | mem server server-fn-churn (solid) |
274.9 KB | 458.7 KB | -40.07% |
| ... | ... | ... | ... | ... | ... |
ℹ️ Only the first 20 benchmarks are displayed. Go to the app to view all benchmarks.
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing test-codspeed-memory-stabilization (300581b) with main (abf9b81)
|
View your CI Pipeline Execution ↗ for commit 300581b
☁️ Nx Cloud last updated this comment at |
🚀 Changeset Version PreviewNo changeset entries found. Merging this PR will not cause a version bump for any packages. |
Summary by CodeRabbit
Benchmark Improvements
Documentation
Tests