Skip to content

Speed up CodeGraphy graph load and interactions#294

Merged
joesobo merged 192 commits into
mainfrom
codex/speed-up-codegraphy
Jun 25, 2026
Merged

Speed up CodeGraphy graph load and interactions#294
joesobo merged 192 commits into
mainfrom
codex/speed-up-codegraphy

Conversation

@joesobo

@joesobo joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Summary

  • Speeds up the large-monorepo graph path end to end: cold indexing, Graph Cache persistence/replay, visible-graph filtering, Graph Scope toggles, warm startup, saved-file updates, Material groups, Godot class names, and TypeScript alias resolution.
  • Keeps the user-facing behavior incremental and deterministic: warm cache paths replay faster, visible graph updates reuse prior work, and saved-file refreshes patch the current graph instead of forcing unnecessary full refresh/broadcast cycles.
  • Preserves the force-directed graph behavior after cached-position reuse: positioned nodes now seed the layout while the interactive physics simulation still runs normally.
  • Removes the temporary performance measurement harness, diagnostics, and iteration notes from the final diff. The reviewable PR now contains the product improvements plus user-focused changesets with the measured before/after stats.
  • Consolidates the release notes into a small set of changesets grouped by user impact instead of many internal implementation slices.

Measured performance

Large CodeGraphy monorepo measurements captured during PR iteration:

Area Baseline Current / best measured Result
Cold monorepo indexing 214.04s initial no-cache baseline 17.28s latest confirmed cold-index run ~12.4x faster
Comparable cold-index JSON anchor 104.67s 17.28s ~6.1x faster after mid-run baseline
File/plugin analysis phase 87,918ms 3,697ms ~23.8x faster
Graph Cache save phase 122,757ms initial phase baseline 10,904ms ~11.3x faster
Graph Cache size 64,638,976 bytes 18,153,472 bytes ~72% smaller
Visible graph: current settings 775ms median 12ms median ~65x faster
Visible graph: folders on 1,369ms median 32ms median ~43x faster
Visible graph: imports off 153ms median 7ms median ~22x faster
Visible graph: search projection 781ms median 12ms median ~65x faster
VS Code Imports Graph Scope toggle 2,983ms repeat-run median 188ms wall-clock median / 54ms in-webview median ~16x faster wall-clock; browser-visible path is near-instant
Repeat startup to first graph 9,917ms 4,614ms ~2.1x faster, with remaining time mostly VS Code/webview frame readiness
First ready webview graph work not instrumented initially build graph 10ms, style 5ms, apply legends 16ms graph math is no longer the startup bottleneck
Material group compute 66ms 38ms ~42% faster
Material group publish total 71ms 39ms ~45% faster
Godot metadata slice cold indexing 104.67s 37.27s ~2.8x faster for the Godot-heavy pass
TypeScript alias optimization cold indexing 37.27s 17.28s ~2.2x faster for the final alias-heavy pass

Regression fix

  • Fixed a post-cleanup regression where reused node positions caused interactive graphs to use cooldownTicks: 0, which rendered nodes but prevented the force simulation from behaving like a live physics graph.
  • Added a webview rendering regression test asserting that positioned interactive graphs keep the normal physics cooldown.

Validation

  • Initial baseline before implementation: pnpm run test passed.
  • Cleanup verification: git diff --check passed.
  • Cleanup verification: strict search for removed harness/instrumentation markers returned no matches.
  • Changeset verification: pnpm changeset status --since origin/main passed.
  • Extension targeted cleanup tests passed: 15 files / 113 tests.
  • Webview targeted cleanup tests passed: 4 files / 58 tests.
  • Release test smoke passed: pnpm run test:release.
  • Extension quality gates passed: pnpm --filter @codegraphy-dev/extension run typecheck, pnpm --filter @codegraphy-dev/extension run lint, and full extension test suite, 1010 files / 6117 tests.
  • Physics regression test was red first, then passed: pnpm --filter @codegraphy-dev/extension exec vitest run --config vitest.config.ts tests/webview/graph/rendering/sharedProps.test.ts.
  • Nearby graph rendering slice passed: shared props, viewport model/view, 2D surface, and 3D surface tests, 5 files / 36 tests.
  • Cleanup and physics-fix commit hooks passed acceptance-spec ownership guard, monorepo typecheck, and lint-staged.
  • Human-owned acceptance specs were not edited.

Caveats / follow-up

  • The graph math and browser-visible interaction path are now very fast, but startup is not fully editor-instant yet. The latest trace still had most remaining startup time in VS Code/webview frame readiness before the graph document was available.
  • The VS Code toggle wall-clock number includes Playwright/DOM polling overhead; the in-webview event delta is the better proxy for user-perceived rendering after the event reaches CodeGraphy.

@changeset-bot

changeset-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 536ebb9

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

This PR includes changesets to release 5 packages
Name Type
@codegraphy-dev/plugin-godot Patch
@codegraphy-dev/core Patch
@codegraphy-dev/extension Patch
@codegraphy-dev/plugin-typescript Patch
@codegraphy-dev/mcp 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

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 1: measurement harness\n\n- Changed: added , a bounded JSON writer/parser for cold-index logs, and a reviewed baseline summary at .\n- Test:

codegraphy@1.0.0 test:release /Users/poleski/Desktop/Projects/CodeGraphyV4/.worktrees/speed-up-codegraphy
node --test tests/release/.test.mjs tests/scripts/.test.mjs

TAP version 13

Subtest: core extension release declares platform-specific VSIX targets

ok 1 - core extension release declares platform-specific VSIX targets

duration_ms: 1.435708
type: 'test'
...

Subtest: package mode creates one target-specific vsce invocation per VSIX target

ok 2 - package mode creates one target-specific vsce invocation per VSIX target

duration_ms: 0.290458
type: 'test'
...

Subtest: publish mode publishes every target-specific VSIX target

ok 3 - publish mode publishes every target-specific VSIX target

duration_ms: 0.123125
type: 'test'
...

Subtest: resolves the VSIX target that matches the host native runtime

ok 4 - resolves the VSIX target that matches the host native runtime

duration_ms: 0.096583
type: 'test'
...

Subtest: rejects cross-target VSIX packaging for host-built Tree-sitter bindings

ok 5 - rejects cross-target VSIX packaging for host-built Tree-sitter bindings

duration_ms: 0.237083
type: 'test'
...

Subtest: allows one explicitly requested target when it matches the host native runtime

ok 6 - allows one explicitly requested target when it matches the host native runtime

duration_ms: 0.05525
type: 'test'
...

Subtest: stages the target LadybugDB native binary before packaging a target VSIX

ok 7 - stages the target LadybugDB native binary before packaging a target VSIX

duration_ms: 2.629
type: 'test'
...

Subtest: resolves target LadybugDB native binary from a fetched optional package when it is not installed

ok 8 - resolves target LadybugDB native binary from a fetched optional package when it is not installed

duration_ms: 1.853
type: 'test'
...

codegraphy.codegraphy-5.8.0-linux-x64.vsix: extension/dist/node_modules/@ladybugdb/core/lbugjs.node is ELF x86-64

codegraphy.codegraphy-5.8.0-linux-x64.vsix: extension/dist/node_modules/@ladybugdb/core/lbugjs.node is ELF x86-64

codegraphy.codegraphy-5.8.0-linux-x64.vsix: extension/dist/node_modules/tree-sitter/build/Release/tree_sitter_runtime_binding.node is ELF x86-64

Subtest: identifies Linux x64 native binaries from ELF headers

ok 9 - identifies Linux x64 native binaries from ELF headers

duration_ms: 0.961792
type: 'test'
...

Subtest: identifies macOS Apple Silicon native binaries from Mach-O headers

ok 10 - identifies macOS Apple Silicon native binaries from Mach-O headers

duration_ms: 0.225084
type: 'test'
...

Subtest: identifies Windows x64 native binaries from PE headers

ok 11 - identifies Windows x64 native binaries from PE headers

duration_ms: 0.206709
type: 'test'
...

Subtest: rejects a linux x64 VSIX with a macOS Apple Silicon Tree-sitter binding

ok 12 - rejects a linux x64 VSIX with a macOS Apple Silicon Tree-sitter binding

duration_ms: 24.754125
type: 'test'
...

Subtest: validates only the requested VSIX artifact targets

ok 13 - validates only the requested VSIX artifact targets

duration_ms: 11.116083
type: 'test'
...

Subtest: performance runner writes bounded JSON metrics

ok 14 - performance runner writes bounded JSON metrics

duration_ms: 7.251583
type: 'test'
...

Subtest: uses the Turbo task hash as the Playwright cache hash for one package

ok 15 - uses the Turbo task hash as the Playwright cache hash for one package

duration_ms: 0.753417
type: 'test'
...

Subtest: combines multiple Playwright task hashes into a stable cache hash

ok 16 - combines multiple Playwright task hashes into a stable cache hash

duration_ms: 8.738292
type: 'test'
...

Subtest: reports Playwright tasks as cached only when every task can replay

ok 17 - reports Playwright tasks as cached only when every task can replay

duration_ms: 0.109125
type: 'test'
...

Subtest: builds a filtered turbo command for Playwright-owning packages

ok 18 - builds a filtered turbo command for Playwright-owning packages

duration_ms: 1.531375
type: 'test'
...

Subtest: passes Playwright args after the turbo passthrough delimiter

ok 19 - passes Playwright args after the turbo passthrough delimiter

duration_ms: 0.081
type: 'test'
...

Subtest: splits turbo args from Playwright passthrough args

ok 20 - splits turbo args from Playwright passthrough args

duration_ms: 0.144875
type: 'test'
...
1..20

tests 20

suites 0

pass 20

fail 0

cancelled 0

skipped 0

todo 0

duration_ms 130.416042 passed all 20 tests;

codegraphy@1.0.0 perf:codegraphy-monorepo /Users/poleski/Desktop/Projects/CodeGraphyV4/.worktrees/speed-up-codegraphy
node scripts/performance/measure-codegraphy-monorepo.mjs --workspace . --index-log reports/performance/codegraphy-index-cold-local-node22-2026-06-22.log generated .\n- Metric before: cold monorepo index log showed 214.04s wall time for 2365 files, 5075 nodes, 9097 edges.\n- Metric after: no runtime optimization yet; harness records the same baseline as , .\n- Next: add phase-level instrumentation / first scoped-refresh optimization so we can compare before and after.

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 1 correction: measurement harness

  • Changed: added perf:codegraphy-monorepo, a bounded JSON writer/parser for cold-index logs, and a reviewed baseline summary at docs/performance/codegraphy-monorepo.md.
  • Test: pnpm run test:release passed all 20 tests; pnpm run perf:codegraphy-monorepo generated reports/performance/monorepo-latest.json.
  • Metric before: cold monorepo index log showed 214.04s wall time for 2365 files, 5075 nodes, 9097 edges.
  • Metric after: no runtime optimization yet; harness records the same baseline as coldIndexMs=214040 and graphCacheBytes=64561152.
  • Next: add phase-level instrumentation and the first scoped-refresh optimization so we can compare before and after.

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 2: scoped refresh fast path

  • Changed: Graph Scope analysis refresh now reuses current cached analysis when every discovered file already satisfies the required cache tiers for current node visibility and active plugins.
  • Test: targeted refresh facade/internal tests passed: 15 tests across 2 files. Extension typecheck passed. Pre-commit acceptance ownership, typecheck, and eslint passed.
  • Metric before: tier-complete scope refresh delegated to refreshWorkspacePipelineAnalysisScope, which calls _analyzeFiles over all discovered files.
  • Metric after: same tier-complete scope refresh performs 0 _analyzeFiles calls, rebuilds graph data from cached analysis, persists metadata, and emits Applying Scope 0/N -> N/N progress.
  • Next: add phase-level indexing diagnostics or measure the real VS Code warm-cache Graph Scope toggle path on the Mac mini.

Measure cold monorepo indexing by phase: analysis 88321ms, graph build 62ms, and Graph Cache save 122757ms on the CodeGraphy monorepo fixture.
@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 3: cold-index phase timings

Commit: cb7d859f4 perf: emit phase timings for core indexing

Verification:

  • node --test tests/scripts/measure-codegraphy-monorepo.test.mjs passed
  • pnpm --filter @codegraphy-dev/core exec vitest run --config vitest.config.ts tests/cli/command.test.ts tests/cli/index/command.test.ts tests/diagnostics/events.test.ts tests/workspace/coreBacked.test.ts passed: 16 tests
  • pnpm --filter @codegraphy-dev/core run typecheck passed
  • pnpm run test:release passed: 21 tests
  • Commit hook also ran acceptance ownership, repo typecheck, and lint-staged successfully

Cold monorepo phase benchmark from no Graph Cache:

  • Total: 213.93s
  • Plugin load: 542ms
  • Plugin init: 1ms
  • File discovery: 1900ms
  • File analysis: 88321ms
  • Graph build: 62ms
  • Graph Cache save: 122757ms
  • Metadata persistence: 4ms
  • Counts: 2367 files, 5078 nodes, 9114 edges
  • Memory: max RSS 3071688704, peak footprint 4200348736

Takeaway: cold load is not graph construction. The measured hot spots are Graph Cache persistence first, then per-file/plugin analysis.

Reduce cold CodeGraphy monorepo indexing from 213.93s to 111.03s by persisting FileAnalysis as the canonical cache row and deriving snapshot symbols/relations from it. Graph Cache save dropped from 122757ms to 15139ms and cache size from 64638976 bytes to 18153472 bytes.
@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 4: canonical Graph Cache persistence

Commit: 1de00982b perf: persist canonical Graph Cache rows

Change:

  • Persist only canonical FileAnalysis rows in the Graph Cache.
  • Derive snapshot symbols/relations from FileAnalysis.analysis when dedicated Symbol/Relation rows are absent.
  • Existing caches with structured rows still read through the old path, so graph query/export surfaces keep their data.

Benchmark on the CodeGraphy monorepo cold index from no Graph Cache:

  • Total wall time: 213.93s -> 111.03s
  • Graph Cache save: 122757ms -> 15139ms
  • Graph Cache size: 64638976 bytes -> 18153472 bytes
  • File analysis is now the dominant cold-load phase: 92850ms

Verification:

  • pnpm --filter @codegraphy-dev/core exec vitest run --config vitest.config.ts tests/graphCache/database tests/indexing/workspace.test.ts tests/workspace/coreBacked.test.ts tests/cli/command.test.ts tests/cli/index/command.test.ts passed: 71 tests
  • pnpm --filter @codegraphy-dev/extension exec vitest run --config vitest.config.ts tests/extension/pipeline/analysis.test.ts tests/extension/pipeline/service/base/state.test.ts tests/extension/graphViewProvider.lifecycle.test.ts --project node passed: 26 tests
  • pnpm run test:release passed: 21 tests
  • pnpm run typecheck passed
  • Commit hook passed acceptance ownership, repo typecheck, and lint-staged

Share file content reads between pre-analysis and cold file analysis. On the CodeGraphy monorepo cold benchmark, wall time moved from 111.03s to 104.81s and analyze-files moved from 92850ms to 87297ms.
@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration 5: reuse content between pre-analysis and analysis

Commit: 03290a6d2 perf: reuse content during index analysis

Change:

  • Core indexing now shares one cached readContent closure between pre-analysis and cold file analysis.
  • Added a regression test proving two cold files are read twice total, not twice per phase.

Benchmark on the CodeGraphy monorepo cold index from no Graph Cache:

  • Total wall time: 111.03s -> 104.81s
  • Analyze phase: 92850ms -> 87297ms
  • Graph Cache save stayed stable: 15139ms -> 14632ms

Verification:

  • pnpm --filter @codegraphy-dev/core exec vitest run --config vitest.config.ts tests/indexing/analysis.test.ts tests/indexing/workspace.test.ts tests/analysis/fileAnalysis/run.test.ts tests/analysis/workspaceFiles.test.ts tests/workspace/coreBacked.test.ts passed: 34 tests
  • pnpm --filter @codegraphy-dev/core run typecheck passed
  • Commit hook passed acceptance ownership, repo typecheck, and lint-staged

Remaining cold-load bottleneck is still file/plugin analysis, now measured at 87297ms.

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration update: Godot class name pre-analysis fast path

Commit: 9e3294e (perf: speed up Godot class name indexing)

What changed:

  • extractGDScriptClassNameDeclarations now uses the existing line/document scanner instead of the GDScript Lezer syntax parser for metadata class_name indexing.
  • Added a regression/perf-contract test that fails if class name extraction calls the syntax parser.
  • Added a patch changeset for @codegraphy-dev/plugin-godot.

Why:

  • CPU profile showed roughly 30s in Godot onPreAnalyze via Lezer recovery while indexing the CodeGraphy monorepo.
  • This metadata path only needs leading class_name declarations; full syntax recovery is unnecessary.

Branch-accurate benchmark note:

  • The normal CLI was loading package plugins from the user's real ~/.codegraphy/plugins.json, which pointed several plugins at an older worktree. For this slice I used a direct Core API cold index with userHomeDir pointed at an isolated plugin cache whose package roots point at this PR worktree.

Exact local-plugin before/after, no Graph Cache:

  • Cold index total: 104.67s -> 37.27s
  • Analyze files: 87,918ms -> 23,352ms
  • Save Graph Cache: 14,058ms -> 11,233ms
  • Max RSS: 2,901,016,576 -> 465,518,592 bytes
  • Peak memory footprint: 4,232,806,784 -> 332,065,728 bytes
  • Files: 2367 -> 2367
  • Nodes: 5079 -> 5079
  • Edges: 9110 -> 9108

Graph diff check:

  • Persisted relationship diff showed only the changed CodeGraphy source imports/calls in packages/plugin-godot/src/gdscript/className.ts (syntaxTree/classNameLine removed, document added). No workspace Godot facts disappeared.

Verification:

  • pnpm --filter @codegraphy-dev/plugin-godot test passed: 60 files / 366 tests.
  • pnpm --filter @codegraphy-dev/plugin-godot run typecheck passed.
  • pnpm --filter @codegraphy-dev/plugin-godot run lint passed.
  • pnpm run typecheck passed.
  • pnpm run lint passed.
  • Pre-commit hook passed acceptance ownership, typecheck, and lint-staged.

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration update: TypeScript alias config no-scan parse

Commit: 595aa15 (perf: skip TypeScript alias config file scans)

What changed:

  • @codegraphy-dev/plugin-typescript now parses tsconfig alias options with a ParseConfigHost that can read config files and extended config files, but returns no project file list.
  • Added a regression/perf-contract test that fails if alias config analysis calls ts.sys.readDirectory.
  • Added a patch changeset for @codegraphy-dev/plugin-typescript.

Why:

  • CPU profile showed about 20s under TypeScript parseJsonConfigFileContent -> getFileNamesFromConfigSpecs -> directory scanning.
  • Alias import analysis only needs compilerOptions.paths / baseUrl; it does not need TypeScript to enumerate every project file.

Branch-accurate isolated-plugin benchmark, no Graph Cache:

  • Previous Godot-fast cold total: 37.27s
  • After this change: 17.28s
  • Analyze files: 23,352ms -> 3,697ms
  • Save Graph Cache: 11,233ms -> 10,904ms
  • Files: 2369 (includes this iteration's test/changeset/docs files)
  • Nodes: 5081
  • Edges: 9108
  • Max RSS: 465,518,592 -> 476,708,864 bytes
  • Peak memory footprint: 332,065,728 -> 328,051,904 bytes

Cumulative cold-index progress on the CodeGraphy monorepo:

  • Initial cold CLI baseline: ~214s
  • Canonical Graph Cache rows: 111.03s
  • Shared content read cache: 104.81s
  • Godot class_name fast path: 37.27s
  • TypeScript alias config no-scan parse: 17.28s

Verification:

  • pnpm --filter @codegraphy-dev/plugin-typescript test passed: 9 files / 36 tests.
  • pnpm --filter @codegraphy-dev/plugin-typescript run typecheck passed.
  • pnpm --filter @codegraphy-dev/plugin-typescript run lint passed.
  • pnpm run typecheck passed.
  • pnpm run lint passed.
  • Pre-commit hook passed acceptance ownership, typecheck, and lint-staged.

@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Measurement update: warm Graph Cache query proxy

Commit: 6743acb (docs: record warm Graph Cache query metric)

Measured requestWorkspaceGraphQuery with report nodes, limit: 1 against the current Graph Cache after the cold-index optimizations.

Result:

  • /usr/bin/time wall time: 0.74s
  • Graph Query diagnostic duration: 601ms
  • Query graph size: 2514 nodes / 9108 edges

Caveat:

  • Cache status reported stale with plugin-signature-changed because the query status path compared against the user's real installed-plugin cache while the benchmark loaded the isolated plugin cache. The query still loaded .codegraphy/graph.lbug and built graph data from it.

Takeaway:

  • Headless warm Core graph load/query is already sub-second after the cold-index work. The remaining user-facing work should move into the VS Code/webview path: first graph payload timing, Graph Scope toggle latency, and visible graph render/apply cost.

Current settings projection improved from 775ms median / 933ms p95 to 22ms median / 26ms p95 on the CodeGraphy monorepo visible-graph benchmark.

Folders-on projection improved from 1369ms median / 1445ms p95 to 31ms median / 32ms p95. Import-edge-hidden projection improved from 153ms median to 17ms median / 18ms p95.
@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration: visible graph filter projection

Changed:

  • Added perf:visible-graph-monorepo to benchmark warm Graph Cache -> visible graph projection scenarios.
  • Compiled filter globs once per derivation in core and extension shared visible graph filtering.
  • Skipped direct edge matching for path-only filter globs, while preserving direct edge kind/id patterns like import and src/*->target#import.

Metric before:

  • Current settings projection: 775ms median / 933ms p95
  • Folders-on Graph Scope projection: 1369ms median / 1445ms p95
  • Import-edge-hidden projection: 153ms median

Metric after, fresh verification on commit 67c3f2f81:

  • Current settings projection: 22ms median / 26ms p95
  • Folders-on Graph Scope projection: 31ms median / 32ms p95
  • Import-edge-hidden projection: 17ms median / 18ms p95
  • Scenario node/edge counts unchanged

Verification:

  • pnpm --filter @codegraphy-dev/core exec vitest run tests/globMatch.test.ts tests/visibleGraph/collapse.test.ts tests/visibleGraph/derive.test.ts --config vitest.config.ts -> 18 passed
  • pnpm --filter @codegraphy-dev/extension exec vitest run --config vitest.config.ts --project node tests/shared/globMatch.test.ts tests/shared/visibleGraph/derive.test.ts tests/shared/visibleGraph/derive.runssearchafterfilterpatternstoreturnsregexerrorsfromsearch.test.ts -> 20 passed
  • pnpm --filter @codegraphy-dev/extension exec vitest run --config vitest.config.ts tests/webview/search/visibleGraphConfig.test.ts -> 3 passed
  • node --test tests/scripts/measure-codegraphy-monorepo.test.mjs -> 3 passed
  • pnpm --filter @codegraphy-dev/core typecheck, pnpm --filter @codegraphy-dev/extension typecheck, pnpm --filter @codegraphy-dev/core lint, pnpm --filter @codegraphy-dev/extension lint -> passed
  • pre-commit repo typecheck + lint-staged -> passed

Next:

  • Validate actual VS Code user-facing toggle timing, then decide whether the next bottleneck is warm Graph Cache query/build (~378-601ms) or webview render/apply latency.

Adds an opt-in Extension Development Host performance runner for first graph render and Graph Scope Imports toggle latency.

Latest measurements: first rendered graph stats 57.2s on the first run and 9.9s on a repeat run; Imports toggle around 3.0s median, which points the next bottleneck at graph surface/runtime rendering.
@joesobo

joesobo commented Jun 22, 2026

Copy link
Copy Markdown
Owner Author

Iteration: VS Code graph-view latency harness

Changed:

  • Added perf:vscode-graph-view, an opt-in Extension Development Host runner for real user-facing graph timings.
  • It launches the CodeGraphy monorepo workspace, waits for rendered graph stats, toggles the Graph Scope Imports edge type through the webview, records bounded JSON metrics, and restores .codegraphy/settings.json afterward.

Metric baseline from commit 09d13950b:

  • First run: VS Code launch 1518ms; Open Graph View -> first rendered stats 57209ms; Imports toggle 3127ms median / 3143ms p95 across 5 samples.
  • Repeat run: VS Code launch 850ms; Open Graph View -> first rendered stats 9917ms; Imports toggle 2983ms median / 3079ms p95 across 2 samples.
  • Rendered stats were stable at 2249 nodes / 5333 connections.

Verification:

  • node --test tests/scripts/measure-vscode-graph-view.test.mjs tests/scripts/measure-codegraphy-monorepo.test.mjs -> 4 passed.
  • pnpm --filter @codegraphy-dev/extension run build:vscode -> passed before running the real VS Code benchmark.
  • pre-commit acceptance spec guard + repo typecheck -> passed.

Next:

  • The headless visible graph path is ~22ms median, but the real webview still needs ~3s for an Imports Graph Scope toggle. Next target is graph surface/runtime/render handoff, likely around rebuilding and handing thousands of graph objects to react-force-graph.

When every interactive graph node already has finite coordinates, render the update without additional force-layout cooldown ticks. Fresh/unpositioned layouts and timeline mode keep their existing cooldowns.

VS Code Graph Scope Imports toggle on the CodeGraphy monorepo improved from the repeat-run 2983ms median / 3079ms p95 baseline to 1925ms median / 2341ms p95 across 5 samples.
@joesobo

joesobo commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

Iteration: settled graph cooldown

  • Changed: positioned interactive graph updates now use 0 force-graph cooldown ticks; fresh/unpositioned layouts and timeline mode keep their existing cooldowns.
  • Tests: shared graph props/rendering surface/viewport shell tests; extension typecheck; extension lint; build:vscode.
  • Metric before: Imports Graph Scope toggle repeat baseline was 2983ms median / 3079ms p95.
  • Metric after: 1925ms median / 2341ms p95 across 5 samples (1622, 1664, 1925, 1936, 2341).
  • Next: remaining ~2s appears to be force-graph graphData handoff/canvas redraw rather than visible-graph filtering or simulation cooldown.

Evaluate the constant 2D arrow color and arrow position once per render/settings update instead of passing callbacks that force-graph invokes for every edge.

VS Code Graph Scope Imports toggle on the CodeGraphy monorepo improved from 1925ms median / 2341ms p95 after the cooldown iteration to 1595ms median / 1620ms p95 across 5 samples.
@joesobo

joesobo commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

Iteration: constant 2D arrow settings

  • Changed: 2D arrow color and arrow position are evaluated once per render/settings update and passed to force-graph as primitive values instead of per-edge callbacks.
  • Tests: focused 2D surface and directional hook tests; extension typecheck; extension lint; build:vscode.
  • Metric before: after the settled-cooldown iteration, Imports Graph Scope toggle was 1925ms median / 2341ms p95.
  • Metric after: 1595ms median / 1620ms p95 across 5 samples (1587, 1595, 1582, 1620, 1607).
  • Next: remaining ~1.6s points at the synchronous force-graph graphData update/full canvas redraw; stable graphData plus linkVisibility is the bigger next slice, but it needs hidden-edge semantics across tooltip/accessibility/export/runtime consumers.

Keep viewport overlay, tooltip, stats, and accessibility updates from re-rendering the force-graph surface when the surface inputs are unchanged.

VS Code graph-view Imports toggle metric on the CodeGraphy monorepo: same-environment control 2891ms median / 3563ms p95; memoized surface 1628ms median / 2252ms p95.

Verified with graph/webview vitest slice, typecheck, lint, and build:vscode.
@joesobo

joesobo commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

Iteration: memoized viewport surface

  • Changed: memoized the graph viewport surface so overlay, tooltip, stats, and accessibility state churn no longer re-renders the 2D/3D force-graph surface when surface inputs are unchanged. Stabilized the 2D render-frame callback with a ref-backed callback so plugin overlays and accessibility publishing still see fresh state.
  • Rejected experiment: keeping full render data stable and hiding filtered edges through force-graph visibility callbacks measured 2918-2922ms median, so it did not improve over the same-environment control.
  • Metric before: fresh same-environment control Imports toggle was 2891ms median / 3563ms p95 across 5 samples.
  • Metric after: memoized viewport surface Imports toggle is 1628ms median / 2252ms p95 across 5 samples.
  • Verification: graph/webview vitest slice 62 files / 322 tests passed; pnpm run typecheck passed; pnpm run lint passed; build:vscode passed.
  • Commit: fd3125b perf: memoize graph viewport surface

Next: isolate the remaining ~1.6s between Graph Scope click and visible stats update with a webview-side timing mark so we can tell how much is force-graph graphData reset vs our runtime work.

Add opt-in webview performance events to the VS Code graph-view benchmark, then use those timings to compile legend glob matchers once per visible graph update instead of compiling them for every node and edge check.

VS Code graph-view Imports toggle metric on the CodeGraphy monorepo: instrumented pre-change run 1748ms median / 2272ms p95; compiled legend matchers rerun 835ms median / 846ms p95. applyLegendRules dropped from roughly 460-490ms per pass to roughly 79-83ms per pass.

Verified with webview app/graph/graphScope/search slice 312 files / 1908 tests, pnpm run typecheck, pnpm run lint, build:vscode, and perf:vscode-graph-view.
@joesobo joesobo marked this pull request as ready for review June 25, 2026 17:51
@joesobo joesobo merged commit ed4966b into main Jun 25, 2026
35 checks passed
@joesobo joesobo deleted the codex/speed-up-codegraphy branch June 25, 2026 17:53
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