Skip to content

fix(core): performance improvements#1964

Open
christian-bromann wants to merge 11 commits intomainfrom
cb/perf-improvements
Open

fix(core): performance improvements#1964
christian-bromann wants to merge 11 commits intomainfrom
cb/perf-improvements

Conversation

@christian-bromann
Copy link
Member

@christian-bromann christian-bromann commented Feb 14, 2026

Summary

Performance optimizations for @langchain/langgraph-core targeting the hottest functions identified by profiling the test suite. These findings were generated using the zeitzeuge profiling tool, which analyzed CPU profiles across the full test run (1085 tests, 60.8s total duration).

1. Eliminated repeated Object.values() allocations

Files: pregel/loop.ts, pregel/runner.ts

Object.values(this.tasks) was called ~11 times per tick() invocation, each creating a fresh array from the same unchanged record. Now cached into a local variable (newTaskList, finishTaskList, matchTaskList, allTasks) and reused throughout. Same treatment applied in runner.ts for Object.values(this.loop.tasks).

2. Hoisted per-call allocations in XXH3 hash

File: hash.ts

  • TextEncoder, the bswap64 scratch DataView/ArrayBuffer, and the hexDigest closure were all being created on every XXH3() call. They're now module-level singletons, eliminating repeated allocations on a hot path.

3. Converted arrays to Sets for O(1) lookups

Files: pregel/utils/config.ts, pregel/algo.ts

  • COPIABLE_KEYS and CONFIG_KEYS arrays were converted to Sets; .includes() replaced with .has().
  • RESERVED array usage in _applyWrites was replaced with a module-level RESERVED_SET for O(1) checks.

4. Replaced Object.entries() with for...in loops

File: pregel/utils/config.ts

Three Object.entries() calls in ensureLangGraphConfig each allocated intermediate [key, value] tuple arrays. Switched to for...in with direct property access to avoid those allocations.

5. Skipped stack trace capture for EmptyChannelError

File: errors.ts

EmptyChannelError is thrown for control flow (checking if a channel has data), not for real exceptions. The constructor now temporarily sets Error.stackTraceLimit = 0 before super() to skip V8's expensive stack-walking, then restores it.

6. Cached sort comparator allocations in _applyWrites

File: pregel/algo.ts

The sort comparator was calling .slice(0, 3) on task paths for every comparison (O(n log n) comparisons = 2n log n array allocations). Now pre-computed into a pathCache Map before sorting.

7. Combined multiple task iterations into a single pass

File: pregel/algo.ts

_applyWrites previously iterated tasks separately for: checking bumpStep, updating versions_seen, and collecting channelsToConsume (via flatMap + filter). These were merged into a single loop.

8. Pre-indexed pendingWrites for O(1) lookups

File: pregel/algo.ts

Introduced _indexPendingWrites() which builds a PendingWritesIndex with:

  • nullResume — the first null-task resume value
  • resumeByTaskId — a Map<string, unknown[]> for per-task resume values
  • successfulWriteTaskIds — a Set<string> for quick "has successful write" checks

This index is computed once in _prepareNextTasks and passed through to _prepareSingleTask and _scratchpad, replacing O(N*M) linear scans with O(1) lookups.

9. Converted recursive tick() to a while loop

File: pregel/loop.ts

The recursive return this.tick({ inputKeys }) call was replaced with continue inside a while (true) loop, avoiding async state machine re-entry overhead and potential stack issues.

10. Gated debug output on stream mode

File: pregel/loop.ts

Debug checkpoint and task output is now only generated when this.stream.modes.has("debug") or the relevant mode is active, avoiding expensive serialization work when no one is listening.

11. Fixed O(N^2) object spread in _first()

File: pregel/loop.ts

The versions_seen[INTERRUPT] update previously spread the entire object inside a loop (O(N^2) allocations). Now spreads once into a new interruptSeen object, then mutates it directly — also fixing a subtle bug with shallow-copied checkpoint references.

12. Cached isResuming getter

File: pregel/loop.ts

The isResuming getter (which iterates channel versions and checks config/metadata) was being called multiple times in _first(). Now cached into a local variable for the first two accesses, with a fresh read for the final CONFIG_KEY_RESUMING assignment.

13. Optimized Promise.race array construction

File: pregel/runner.ts

Instead of Promise.race([...Object.values(executingTasksMap), ...abortPromise, barrier.wait]) (which spreads into a new array every iteration), the promises array is now built once with push() calls.

@changeset-bot
Copy link

changeset-bot bot commented Feb 14, 2026

⚠️ No Changeset found

Latest commit: de4e716

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

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

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 14, 2026

Open in StackBlitz

@langchain/langgraph-checkpoint

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint@1964

@langchain/langgraph-checkpoint-mongodb

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-mongodb@1964

@langchain/langgraph-checkpoint-postgres

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-postgres@1964

@langchain/langgraph-checkpoint-redis

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-redis@1964

@langchain/langgraph-checkpoint-sqlite

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-sqlite@1964

@langchain/langgraph-checkpoint-validation

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-validation@1964

create-langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/create-langgraph@1964

@langchain/langgraph-api

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-api@1964

@langchain/langgraph-cli

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cli@1964

@langchain/langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph@1964

@langchain/langgraph-cua

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cua@1964

@langchain/langgraph-supervisor

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-supervisor@1964

@langchain/langgraph-swarm

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-swarm@1964

@langchain/langgraph-ui

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-ui@1964

@langchain/langgraph-sdk

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-sdk@1964

commit: de4e716

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