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,