perf(kernel): direct-to-store decode — buffers flow to the store worker, main thread never materializes nodes

Kernel-routed files ship their flat tables from the parse worker to the
store worker as buffers (tryKernelExtractRaw → kernelBuffers on the
result → KernelStoreBundle); the store worker decodes and finalizes
(finalizeStoreBundle shared with the object path so filter semantics
can never drift). Files with applicable framework extract() hooks keep
the decoded path; the no-writer fallback materializes via
materializeKernelResult. Byte-identical dumps re-verified on dubbo,
excalidraw, express, gson; full suite green (2,467).

Measurement (plan §4d): dubbo's parse-loop wall is 94% store-writer
busy time — the many-core fresh-index wall is single-writer SQLite
ingest, not extraction or main-thread work. d2s improves the writer
lane ~11% (structured-clone deserialization avoided on the writer) and
frees the main thread; the remaining many-core gap is a
store-architecture arc (deferred index builds, multi-file
transactions, buffer→bind), out of the kernel project's scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Colby McHenryandClaude Fable 5 committed 2026-07-16 23:38:32 -05:00
1 parent 03d54e47a1
commit 28068fa0f1
7 files changed
+277 -39

No files matched your search

+52 -33
View File
@@ -23,7 +23,8 @@ import {
import { QueryBuilder } from '../db/queries';
import { extractFromSource } from './tree-sitter';
import { ParseWorkerPool, resolveParsePoolSize, resolveParseTimeoutMs } from './parse-pool';
import { StoreWriter, StoreBundle } from './store-writer';
import { StoreWriter, StoreBundle, finalizeStoreBundle } from './store-writer';
import { materializeKernelResult } from './kernel';
import { detectLanguage, isSourceFile, isLanguageSupported, isFileLevelOnlyLanguage, initGrammars, loadGrammarsForLanguages, readGrammarWasmBytes } from './grammars';
import { loadExtensionOverrides, loadIncludeIgnoredPatterns, loadExcludePatterns, loadIncludePatterns } from '../project-config';
import { isCodeGraphDataDir } from '../directory';
@@ -1706,16 +1707,34 @@ export class ExtractionOrchestrator {
const bp = walBackpressure?.();
if (bp) await bp;
// Kernel deferred-decode results carry table sizes in kernelCounts
// (their object arrays are empty — decode happens at the store).
const nodeCount = result.kernelCounts?.nodes ?? result.nodes.length;
const edgeCount = result.kernelCounts?.edges ?? result.edges.length;
// Store: on the writer thread when active (fresh DB — bundles applied
// in the same file order this chain dispatches them), else on the main
// thread (SQLite connections are per-thread).
if (result.nodes.length > 0 || result.errors.length === 0) {
if (nodeCount > 0 || result.errors.length === 0) {
const language = detectLanguage(filePath, content, overrides);
if (storeWriter) {
storeWriter.send(this.buildFreshStoreBundle(filePath, content, language, stats, result));
if (result.kernelBuffers) {
// Buffers go to the writer as-is; the worker decodes + finalizes.
// The main thread's only per-file work stays O(1) + the content hash.
storeWriter.send({
kernel: true,
filePath,
language,
buffers: result.kernelBuffers,
file: this.buildFileRecord(filePath, content, language, stats, nodeCount, result.errors),
});
} else {
storeWriter.send(this.buildFreshStoreBundle(filePath, content, language, stats, result));
}
await storeWriter.waitBelow(STORE_WRITER_WINDOW);
} else {
await this.storeExtractionResult(filePath, content, language, stats, result, commitYield);
const materialized = materializeKernelResult(result, filePath, language);
await this.storeExtractionResult(filePath, content, language, stats, materialized, commitYield);
}
}
@@ -1726,10 +1745,10 @@ export class ExtractionOrchestrator {
errors.push(...result.errors);
}
if (result.nodes.length > 0) {
if (nodeCount > 0) {
filesIndexed++;
totalNodes += result.nodes.length;
totalEdges += result.edges.length;
totalNodes += nodeCount;
totalEdges += edgeCount;
} else if (result.errors.some((e) => e.severity === 'error')) {
filesErrored++;
} else {
@@ -2383,6 +2402,27 @@ export class ExtractionOrchestrator {
* check, no cross-file edge snapshot (both are re-index concerns — a fresh
* database has neither). Filters mirror storeExtractionResult exactly.
*/
/** The FileRecord for a fresh-index store (nodeCount is the PRE-filter count). */
private buildFileRecord(
filePath: string,
content: string,
language: Language,
stats: fs.Stats,
nodeCount: number,
resultErrors: ExtractionResult['errors']
): FileRecord {
return {
path: filePath,
contentHash: hashContent(content),
language,
size: stats.size,
modifiedAt: stats.mtimeMs,
indexedAt: Date.now(),
nodeCount,
errors: resultErrors.length > 0 ? resultErrors : undefined,
};
}
private buildFreshStoreBundle(
filePath: string,
content: string,
@@ -2390,33 +2430,12 @@ export class ExtractionOrchestrator {
stats: fs.Stats,
result: ExtractionResult
): StoreBundle {
const validNodes = result.nodes.filter((n) => n.id && n.kind && n.name && n.filePath && n.language);
const insertedIds = new Set(validNodes.map((n) => n.id));
const validEdges = result.edges.filter(
(e) => insertedIds.has(e.source) && insertedIds.has(e.target)
return finalizeStoreBundle(
result,
filePath,
language,
this.buildFileRecord(filePath, content, language, stats, result.nodes.length, result.errors)
);
const validRefs = result.unresolvedReferences
.filter((ref) => insertedIds.has(ref.fromNodeId))
.map((ref) => ({
...ref,
filePath: ref.filePath ?? filePath,
language: ref.language ?? language,
}));
return {
nodes: validNodes,
edges: validEdges,
refs: validRefs,
file: {
path: filePath,
contentHash: hashContent(content),
language,
size: stats.size,
modifiedAt: stats.mtimeMs,
indexedAt: Date.now(),
nodeCount: result.nodes.length,
errors: result.errors.length > 0 ? result.errors : undefined,
},
};
}
/**