feat(libs): parallel s-expr parse + mimalloc + copy-as-is transport — symbol chooser cold 274s→44s
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ffd1cb1fc3
commit
4025b05218
5 changed files with 40 additions and 20 deletions
|
|
@ -157,14 +157,15 @@ export function cdnLibsSource(
|
|||
},
|
||||
async getAllItems(
|
||||
libId: string,
|
||||
): Promise<Array<{ kind: string; name: string; body: string }>> {
|
||||
// One bulk merged read of the whole lib (the IDB cache after cold bundle),
|
||||
// so the WASM plugin hydrates in a single crossing instead of N gets.
|
||||
): Promise<Array<{ kind: string; name: string; body: Uint8Array }>> {
|
||||
// One bulk merged read of the whole lib (the IDB cache after cold bundle).
|
||||
// "Copy as-is": hand back the raw IDB bytes (no TextDecoder) so the provider
|
||||
// frames them and the bridge memcpy's them into wasm with no string/JSON
|
||||
// detour — the plugin slices them straight out of the wasm heap.
|
||||
const stack = await openStack(libId);
|
||||
const dec = new TextDecoder();
|
||||
return [...(await stack.readAll())].map(([path, bytes]) => {
|
||||
const { kind, name } = splitPath(path);
|
||||
return { kind, name, body: dec.decode(bytes) };
|
||||
return { kind, name, body: bytes };
|
||||
});
|
||||
},
|
||||
async getItemBody(
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export interface LibsSource {
|
|||
*/
|
||||
getAllItems?(
|
||||
libId: string,
|
||||
): Promise<Array<{ kind: string; name: string; body: string }>>;
|
||||
): Promise<Array<{ kind: string; name: string; body: Uint8Array }>>;
|
||||
/**
|
||||
* Pre-warm this source's per-lib caches (IndexedDB bundles) WITHOUT touching the
|
||||
* WASM runtime — call it in parallel with the wasm download so the editor's
|
||||
|
|
@ -104,7 +104,9 @@ export type KicadLibsRequest = (
|
|||
lib: string,
|
||||
arg: string,
|
||||
kind?: string,
|
||||
) => Promise<string | null>;
|
||||
// "bodies" returns a framed Uint8Array (raw item bytes, copied as-is across the
|
||||
// bridge); every other op returns a string (or null).
|
||||
) => Promise<string | Uint8Array | null>;
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
|
|
@ -160,12 +162,14 @@ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
|
|||
async function fallbackGetAllItems(
|
||||
source: LibsSource,
|
||||
libId: string,
|
||||
): Promise<Array<{ kind: string; name: string; body: string }>> {
|
||||
): Promise<Array<{ kind: string; name: string; body: Uint8Array }>> {
|
||||
const items = await source.listItems(libId);
|
||||
const out: Array<{ kind: string; name: string; body: string }> = [];
|
||||
const enc = new TextEncoder();
|
||||
const out: Array<{ kind: string; name: string; body: Uint8Array }> = [];
|
||||
for (const it of items) {
|
||||
const body = await source.getItemBody(libId, it.kind, it.name);
|
||||
if (body != null) out.push({ kind: it.kind, name: it.name, body });
|
||||
if (body != null)
|
||||
out.push({ kind: it.kind, name: it.name, body: enc.encode(body) });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
|
@ -246,10 +250,26 @@ export function installLibsProvider(
|
|||
const all = source.getAllItems
|
||||
? await source.getAllItems(id)
|
||||
: await fallbackGetAllItems(source, id);
|
||||
const items = all
|
||||
.filter((i) => i.kind === kind)
|
||||
.map((i) => ({ name: i.name, body: i.body }));
|
||||
return JSON.stringify({ [key]: items });
|
||||
const items = all.filter((i) => i.kind === kind);
|
||||
// "Copy as-is" framing: a one-line JSON header (names + UTF-8 byte
|
||||
// lengths), a newline, then every body's RAW bytes concatenated — no
|
||||
// JSON escaping. The C++ bridge memcpy's this straight into the wasm
|
||||
// heap; the plugin parses the small header and slices the bodies, so
|
||||
// none of the (hundreds of MB of) s-expr gets un-escaped.
|
||||
const header = JSON.stringify({
|
||||
[key]: items.map((i) => ({ name: i.name, len: i.body.length })),
|
||||
});
|
||||
const headerBytes = new TextEncoder().encode(header + "\n");
|
||||
const total =
|
||||
headerBytes.length + items.reduce((n, i) => n + i.body.length, 0);
|
||||
const out = new Uint8Array(total);
|
||||
out.set(headerBytes, 0);
|
||||
let off = headerBytes.length;
|
||||
for (const i of items) {
|
||||
out.set(i.body, off);
|
||||
off += i.body.length;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
const items = await source.listItems(id);
|
||||
const names = items
|
||||
|
|
|
|||
|
|
@ -54,16 +54,15 @@ export function syncedLibsSource(
|
|||
}
|
||||
},
|
||||
async getAllItems(): Promise<
|
||||
Array<{ kind: string; name: string; body: string }>
|
||||
Array<{ kind: string; name: string; body: Uint8Array }>
|
||||
> {
|
||||
// Bulk merged read across the opaque layer stack (origin + mirror overlay),
|
||||
// top-wins — the mirror invariant readAll() preserves. One crossing, no
|
||||
// per-item gets.
|
||||
// per-item gets. "Copy as-is": raw bytes (no TextDecoder) — see cdn-source.
|
||||
const { stack } = await ensure();
|
||||
const dec = new TextDecoder();
|
||||
return [...(await stack.readAll())].map(([path, bytes]) => {
|
||||
const { kind, name } = splitPath(path);
|
||||
return { kind, name, body: dec.decode(bytes) };
|
||||
return { kind, name, body: bytes };
|
||||
});
|
||||
},
|
||||
async getItemBody(_id, kind, name): Promise<string | null> {
|
||||
|
|
|
|||
Loading…
Reference in a new issue