Quantify where workspace-load time goes and why it collides with interactive requests (specifically find all references). Deliverable is a profiled understanding with bottleneck rankings, not necessarily code changes.
PR #553 (W-23354947) landed at commit a96d04c25, providing:
- OTEL span registry with workspace-specific spans
- Worker + coordinator distributed tracing
- CPU/heap profiling service
- Runbooks:
WORKSPACE_LOAD_TESTING.md,WORKSPACE_LOAD_TRACING.md,VERIFY_TRACING.md - Spans exported to
~/.sf/vscode-spans/*.jsonl - trace-debugger agent for span analysis
- Feature worktree:
feature/W-23448544-workspace-load-perf-profile - Based on:
main@a96d04c25(includes all tracing infrastructure) - Path:
/Users/peter.hale/git/apex-ls-perf-workspace-load
- dreamhouse-lwc - Named in WI, anecdotally shows "minutes" to load
- apex-recipes - Larger codebase
- apex-perf-project - Performance-focused test repo
{
"apex.performance.enableWorkspaceLoadOnStartup": true,
"apex.trace.server": "verbose"
}scripts/profile-workspace-load.sh
Steps:
- Clear old span files (
~/.sf/vscode-spans/*.jsonl) - Launch VSCode Extension Development Host
- Open test project (triggers workspace load on startup)
- Wait for load completion ("Apex: Ready" in status bar)
- Collect and summarize span data
Alternatively, use VS Code launch config "Run Extension":
- F5 in this workspace
- In Extension Development Host, open test project
- Monitor status bar for workspace load progress
- Analyze spans after completion
- Measure total
workspace.load.totalduration - Break down by phase:
workspace.batch.decodeworkspace.batch.ingestChunkworkspace.batch.compileChunkworkspace.crossFileEnrichment
- Identify per-file outliers (
worker.compilation.batchCompile.file)
- Start workspace load
- Trigger find all references mid-load
- Measure:
- References latency during load vs. idle
coldReadGate.waittime (evidence of contention)- Request pool saturation
From tracing.ts:
workspace.load.total- End-to-end loadworkspace.batch.decode- Batch decodingworkspace.batch.ingestChunk- Chunk ingestion (data-owner)workspace.batch.compileChunk- Chunk compilationworkspace.crossFileEnrichment- Cross-file enrichmentcoldReadGate.wait- Contention indicatorworker.compilation.batchCompile.file- Per-file compile cost
Both workspace batch compilation AND find-all-references route through the request pool worker:
- Routing map: WorkerCoordinator.ts:619-654
- References config: Priority.Low, 15s timeout, 0 retries (ServiceConfiguration.ts:90-130)
References does:
- Full-detail cursor recompile
- Standalone parse of every lexical candidate See: worker.platform.shared.ts:2090-2229
- Coordinator: LCSAdapter.ts:586-602
- Handler: WorkspaceBatchHandler.ts:594-1036
- Chunk size: 100 files per batch
- Send concurrency: clamped to 2, yields between batches (workspace-loader.ts:335)
- Coordinator (LCSAdapter) - Orchestrates batches
- Data-owner - Storage + ingest
- Compilation - Parse + symbol tables
- Request pool - LSP requests + batch compile chunks
- Resource loader - Stdlib + metadata
# Option A: Automated
./scripts/profile-workspace-load.sh ~/git/dreamhouse-lwc
# Option B: Manual
rm -rf ~/.sf/vscode-spans/*.jsonl
code --extensionDevelopmentPath=./packages/apex-lsp-vscode-extension ~/git/dreamhouse-lwc
# Wait for load, then analyze# Count spans
cat ~/.sf/vscode-spans/*.jsonl | wc -l
# Top span types
cat ~/.sf/vscode-spans/*.jsonl | jq -r '.name' | sort | uniq -c | sort -rn | head -20
# Workspace-specific spans
cat ~/.sf/vscode-spans/*.jsonl | jq -r 'select(.name | test("workspace")) | .name' | sort | uniq -c
# Slow operations (>100ms)
cat ~/.sf/vscode-spans/*.jsonl | jq 'select(.duration > 100000000)' | jq -r '[.name, .duration/1000000 | tostring + "ms"] | @tsv'
# Per-file compile costs
cat ~/.sf/vscode-spans/*.jsonl | jq 'select(.name == "worker.compilation.batchCompile.file") | {file: .attributes.file, duration_ms: (.duration / 1000000)}' | jq -s 'sort_by(.duration_ms) | reverse | .[:10]'Can you analyze the workspace load traces in ~/.sf/vscode-spans/ and identify:
1. Total workspace.load.total duration
2. Critical path (longest chain of dependent spans)
3. Per-phase breakdown (decode, ingest, compile, enrichment)
4. Top 10 slowest per-file compiles
5. Evidence of contention (coldReadGate.wait spans)
6. Redundant work (files compiled multiple times)
- Batch compile chunks + interactive references both contend for request pool
- References experience elevated latency during load
coldReadGate.waitspans indicate blocked requests
- Large files or complex inheritance hierarchies dominate compile time
- Symbol table construction (
addSymbolTable) is the hot path
- Files compiled during batch load, then recompiled by references
- No shared compilation result cache
- Clamped to 2 concurrent batches
- Underutilizes worker pool (4-6 workers available)
- Bottleneck ranking (critical path, slowest operations)
- Contention evidence (references latency, coldReadGate.wait)
- Quantified per-phase breakdown
- Candidate optimizations sequenced as follow-up WIs
Example optimizations:
- Isolate batch compilation from interactive requests (separate worker or priority queue)
- Tune chunk size / send concurrency
- Cache compilation results to avoid redundant recompiles
- Stream early symbol data to unblock references sooner
Summary of findings, critical-path timings, span evidence, and recommended follow-up work items.
- Worktree created on
feature/W-23448544-workspace-load-perf-profile - Extension builds successfully (
npm run compile) - Test project settings configured
- Profiling run produces spans in
~/.sf/vscode-spans/*.jsonl - Spans include
workspace.load.totaland child spans - trace-debugger yields critical-path breakdown
- Findings documented with span evidence