mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 08:35:07 +08:00
Tune LLVM splitting for dev libraries (#193)
- Lower the dev-library split floor and bucket target for faster localized rebuilds. - Preserve executable, release, sanitized, and cache-validation behavior.
This commit is contained in:
@@ -3,7 +3,7 @@ import { mkdtemp, readFile, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { afterAll, expect, test } from "vitest";
|
||||
import { splitLlvmProgram } from "./split.js";
|
||||
import { splitLlvmLibraryProgram, splitLlvmProgram } from "./split.js";
|
||||
|
||||
const scratch: string[] = [];
|
||||
afterAll(async () => {
|
||||
@@ -77,6 +77,20 @@ test("thread-local program state conservatively keeps the single-TU path", () =>
|
||||
expect(splitLlvmProgram(tls, { minimumBytes: 0, targetBytes: 64 * 1024 })).toBeNull();
|
||||
});
|
||||
|
||||
test("dev libraries split at the measured 2MB crossover while executables retain 4MB", () => {
|
||||
const body = Array.from({ length: 700 }, (_, i) =>
|
||||
`define internal i64 @library_pad_${i}() #0 {\nentry:\n ; ${"x".repeat(3072)}\n ret i64 ${i}\n}\n`,
|
||||
).join("\n");
|
||||
const source = SAMPLE.replace("define i64 @public_entry", `${body}\ndefine i64 @public_entry`);
|
||||
expect(Buffer.byteLength(source)).toBeGreaterThan(2 * 1024 * 1024);
|
||||
expect(Buffer.byteLength(source)).toBeLessThan(4 * 1024 * 1024);
|
||||
expect(splitLlvmProgram(source)).toBeNull();
|
||||
expect(splitLlvmLibraryProgram(source)?.shards).toHaveLength(5);
|
||||
|
||||
const belowCrossover = source.slice(0, Math.floor(1.9 * 1024 * 1024));
|
||||
expect(splitLlvmLibraryProgram(belowCrossover)).toBeNull();
|
||||
});
|
||||
|
||||
test("every shard compiles and its merged object exposes only canonical public definitions", async () => {
|
||||
const body = Array.from({ length: 120 }, (_, i) =>
|
||||
`define internal i64 @pad_${i}() #0 {\nentry:\n ; ${"x".repeat(2048)}\n ret i64 ${i}\n}\n`,
|
||||
|
||||
@@ -44,6 +44,14 @@ export interface LlvmProgramSplitOptions {
|
||||
|
||||
const DEFAULT_TARGET_BYTES = 2 * 1024 * 1024;
|
||||
const DEFAULT_MINIMUM_BYTES = 4 * 1024 * 1024;
|
||||
// Library edits pay a relocatable merge/archive cost after shard compilation,
|
||||
// but their stable per-bucket object cache starts winning earlier than the
|
||||
// executable lane's whole-program posture. Node 24 Linux profiling across
|
||||
// 1.62–3.24MB generated modules found the crossover at roughly 2MB; four
|
||||
// function buckets were the best general tradeoff for the 2.15MB target and
|
||||
// remained profitable through 3.24MB. Executables retain the defaults above.
|
||||
const LIBRARY_TARGET_BYTES = 768 * 1024;
|
||||
const LIBRARY_MINIMUM_BYTES = 2 * 1024 * 1024;
|
||||
|
||||
interface FunctionDef {
|
||||
source: string;
|
||||
@@ -236,3 +244,11 @@ export function splitLlvmProgram(
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
/** Dev-library policy tuned independently from executable splitting. */
|
||||
export function splitLlvmLibraryProgram(source: string): LlvmProgramSplit | null {
|
||||
return splitLlvmProgram(source, {
|
||||
minimumBytes: LIBRARY_MINIMUM_BYTES,
|
||||
targetBytes: LIBRARY_TARGET_BYTES,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import { basename, dirname, join, resolve } from "node:path";
|
||||
import { buildCacheRoot, CcCompileError, compileC, compileLibArchive, executableNativeEnvironmentFingerprint, mobileLibraryTarget, mobileTargetRefusal, prepareBuildCacheRoot, pruneBuildCache, resolveCc, targetPlatform } from "./backend/cc.js";
|
||||
import { emitModule } from "./backend/emission/emitter.js";
|
||||
import { emitLlvmModule, LlvmUnsupportedError } from "./backend/llvm/emitter.js";
|
||||
import { splitLlvmProgram } from "./backend/llvm/split.js";
|
||||
import { splitLlvmLibraryProgram, splitLlvmProgram } from "./backend/llvm/split.js";
|
||||
import { rebaseLibrarySourceComments, replaceLibraryIdentity, stripLibraryIdentity, stripLibrarySourceComments } from "./backend/library-identity.js";
|
||||
import { checkerPanicDiag, ffiNativeBuildDiag, libAsyncExportDiag, libAsyncSurfaceDiag, libExportUnresolvedDiag, libGenericExportDiag, libIntBoundaryDiag, libNpmIneligibleDiag, libSidecarDiag, libUnmappableSignatureDiag, iceDiag, isCheckerPanic, LIB_INBOUND_BYTES_TRAP_CODE, LIB_RUNTIME_TRAP_CODES, type ScrDiagnostic } from "./diagnostics/diagnostic.js";
|
||||
import { checkLibraryIntegerSlots, classSeed, hasIntSlots, numberCarrierKind, type FnIntSlots, type IntSlotConfig } from "./library/int-infer.js";
|
||||
@@ -1748,7 +1748,7 @@ async function compileLibraryNative(
|
||||
}
|
||||
const llvmSplit =
|
||||
profile.emission === "llvm" && profile.optimization === "dev" && !sanitize && programSource !== undefined
|
||||
? splitLlvmProgram(programSource)
|
||||
? splitLlvmLibraryProgram(programSource)
|
||||
: null;
|
||||
await compileLibArchive({
|
||||
cPath,
|
||||
|
||||
Reference in New Issue
Block a user