diff --git a/docs/content/docs/how-it-works.mdx b/docs/content/docs/how-it-works.mdx index 0475e311e..8196460cf 100644 --- a/docs/content/docs/how-it-works.mdx +++ b/docs/content/docs/how-it-works.mdx @@ -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. diff --git a/packages/compiler/src/backend/native-codegen-core.ts b/packages/compiler/src/backend/native-codegen-core.ts index d75dc2097..e1e726ef4 100644 --- a/packages/compiler/src/backend/native-codegen-core.ts +++ b/packages/compiler/src/backend/native-codegen-core.ts @@ -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))); } diff --git a/packages/compiler/src/backend/native-codegen.test.ts b/packages/compiler/src/backend/native-codegen.test.ts index ff0224746..fdee2e5ca 100644 --- a/packages/compiler/src/backend/native-codegen.test.ts +++ b/packages/compiler/src/backend/native-codegen.test.ts @@ -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", diff --git a/packages/compiler/src/index.ts b/packages/compiler/src/index.ts index b002e2437..c41653565 100644 --- a/packages/compiler/src/index.ts +++ b/packages/compiler/src/index.ts @@ -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, diff --git a/packages/compiler/src/native/compiler.ts b/packages/compiler/src/native/compiler.ts index 19351a81a..fafae4f9d 100644 --- a/packages/compiler/src/native/compiler.ts +++ b/packages/compiler/src/native/compiler.ts @@ -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,