mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-02 07:43:42 +08:00
Compile dev executables of more than 64 KiB in partitions (#2440)
This commit is contained in:
@@ -42,7 +42,7 @@ Parsing, type checking, lowering, and LLVM IR generation run one after another o
|
||||
|
||||
### Partitions
|
||||
|
||||
The LLVM helper divides an executable's LLVM IR into partitions by its size. Up to 1 MiB of LLVM text is one partition, and up to 2 MiB is four. Larger programs get one partition per 4 MiB of LLVM text, rounded up to a power of two, and at least 8 and at most 64. `--emit=llvm` writes this text, so its file size shows how many partitions a program gets.
|
||||
The LLVM helper divides an executable's LLVM IR into partitions by its size. Up to 1 MiB of LLVM text is one partition, and up to 2 MiB is four. Larger programs get one partition per 4 MiB of LLVM text, rounded up to a power of two, and at least 8 and at most 64. `--emit=llvm` writes this text, so its file size shows how many partitions a program gets. `dev` executables, whose partitions are compiled on their own with nothing imported, are divided from 64 KiB: up to 128 KiB is four partitions, and larger `dev` programs get at least 8.
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
|
||||
@@ -14,20 +14,32 @@ const PROGRAM_PARTITION_BYTES = 4 * 1024 * 1024;
|
||||
const MIN_PARTITION_BYTES = 512 * 1024;
|
||||
const MIN_PROGRAM_PARTITIONS = 8;
|
||||
const MAX_PROGRAM_PARTITIONS = 64;
|
||||
/** SINGLE_PARTITION_BYTES and MIN_PARTITION_BYTES of unoptimized (dev)
|
||||
* executables. */
|
||||
const UNOPTIMIZED_SINGLE_PARTITION_BYTES = 64 * 1024;
|
||||
const UNOPTIMIZED_MIN_PARTITION_BYTES = 32 * 1024;
|
||||
|
||||
/** Executable objects larger than SINGLE_PARTITION_BYTES are divided into
|
||||
* partitions that the helper simplifies, optimizes and compiles concurrently,
|
||||
* importing small functions across partitions for inlining, and reuses
|
||||
* individually across builds. A divided module gets at least
|
||||
* MIN_PROGRAM_PARTITIONS so that mid-size programs use several cores. The
|
||||
* count is a power of two that depends only on the module's size, on every
|
||||
* target, so artifacts never vary with the host and ordinary edits keep every
|
||||
* function in its partition. */
|
||||
export function nativeProgramPartitions(llvmBytes: number): number {
|
||||
if (llvmBytes <= SINGLE_PARTITION_BYTES) return 1;
|
||||
* count is a power of two that depends only on the module's size and
|
||||
* whether it is optimized, on every target, so artifacts never vary with the
|
||||
* host and ordinary edits keep every function in its partition.
|
||||
*
|
||||
* Unoptimized (dev) partitions are compiled on their own with nothing
|
||||
* imported, so they cost no run time and an edit compiles only the partitions
|
||||
* whose functions it changes: dev executables are divided from
|
||||
* UNOPTIMIZED_SINGLE_PARTITION_BYTES, at least
|
||||
* UNOPTIMIZED_MIN_PARTITION_BYTES each. */
|
||||
export function nativeProgramPartitions(llvmBytes: number, unoptimized = false): number {
|
||||
const single = unoptimized ? UNOPTIMIZED_SINGLE_PARTITION_BYTES : SINGLE_PARTITION_BYTES;
|
||||
const least = unoptimized ? UNOPTIMIZED_MIN_PARTITION_BYTES : MIN_PARTITION_BYTES;
|
||||
if (llvmBytes <= single) return 1;
|
||||
const required = Math.max(
|
||||
Math.ceil(llvmBytes / PROGRAM_PARTITION_BYTES),
|
||||
Math.min(MIN_PROGRAM_PARTITIONS, Math.ceil(llvmBytes / MIN_PARTITION_BYTES)),
|
||||
Math.min(MIN_PROGRAM_PARTITIONS, Math.ceil(llvmBytes / least)),
|
||||
);
|
||||
return Math.min(MAX_PROGRAM_PARTITIONS, 2 ** Math.ceil(Math.log2(required)));
|
||||
}
|
||||
|
||||
@@ -408,6 +408,14 @@ test("program partitions depend only on module size", () => {
|
||||
expect(nativeProgramPartitions(32 * megabyte + 1)).toBe(16);
|
||||
expect(nativeProgramPartitions(100 * megabyte)).toBe(32);
|
||||
expect(nativeProgramPartitions(1024 * megabyte)).toBe(64);
|
||||
// Unoptimized (dev) programs are divided from 64 KiB.
|
||||
const kilobyte = 1024;
|
||||
expect(nativeProgramPartitions(64 * kilobyte, true)).toBe(1);
|
||||
expect(nativeProgramPartitions(64 * kilobyte + 1, true)).toBe(4);
|
||||
expect(nativeProgramPartitions(128 * kilobyte, true)).toBe(4);
|
||||
expect(nativeProgramPartitions(128 * kilobyte + 1, true)).toBe(8);
|
||||
expect(nativeProgramPartitions(megabyte, true)).toBe(8);
|
||||
expect(nativeProgramPartitions(32 * megabyte + 1, true)).toBe(16);
|
||||
expect(nativePartitionPaths("/build.d/program.o", 3)).toEqual([
|
||||
"/build.d/program.o",
|
||||
"/build.d/program.part1.o",
|
||||
|
||||
@@ -784,7 +784,9 @@ async function emitNativeProgramObject(
|
||||
// An optimized program that stays one partition is optimized whole and
|
||||
// generates code for its parts concurrently.
|
||||
const codegenParts = single || opts.optimization === "dev" ? 1 : nativeCodegenParts(llvmBytes);
|
||||
const partitions = single ? 1 : Math.max(nativeProgramPartitions(llvmBytes), codegenParts);
|
||||
const partitions = single
|
||||
? 1
|
||||
: Math.max(nativeProgramPartitions(llvmBytes, opts.optimization === "dev"), codegenParts);
|
||||
try {
|
||||
const artifact = await emitNativeArtifact({
|
||||
outputPath: linkPath,
|
||||
|
||||
@@ -386,7 +386,9 @@ export class NativeCompiler {
|
||||
// An optimized program that stays one partition is optimized whole and
|
||||
// generates code for its parts concurrently.
|
||||
const codegenParts = library || optimization === "dev" ? 1 : nativeCodegenParts(size);
|
||||
const partitions = library ? 1 : Math.max(nativeProgramPartitions(size), codegenParts);
|
||||
const partitions = library
|
||||
? 1
|
||||
: Math.max(nativeProgramPartitions(size, optimization === "dev"), codegenParts);
|
||||
return this.emitObject(
|
||||
input,
|
||||
output,
|
||||
|
||||
Reference in New Issue
Block a user