mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-01 07:33:12 +08:00
Merge remote-tracking branch 'origin/perf/static-objects' into HEAD
# Conflicts: # packages/compiler/src/frontend/pipeline.ts # packages/compiler/src/frontend/shape-inference/overlay.ts # tests/harness/npm.test.ts
This commit is contained in:
@@ -180,6 +180,16 @@ function generateInput(kind) {
|
||||
["sku-" + i, "category-" + (i % 17), "product " + i, i % 40, 199 + (i % 5000)].join("\t"),
|
||||
).join("\n") + "\n",
|
||||
);
|
||||
} else if (kind === "line-reader") {
|
||||
// About 67 MB of access-log lines; every 50th path is not ASCII.
|
||||
writeFileSync(
|
||||
path,
|
||||
Array.from(
|
||||
{ length: 600_000 },
|
||||
(_, i) =>
|
||||
`10.${i % 251}.${i % 7}.${i % 13} - - [24/Oct/2026:${String(i % 24).padStart(2, "0")}:${String(i % 60).padStart(2, "0")}:00 +0000] "${i % 9 === 0 ? "POST" : "GET"} /${i % 50 === 0 ? "caf\u00e9" : "api"}/items/${i % 997}?page=${i % 31} HTTP/1.1" ${i % 23 === 0 ? 404 : 200} ${(i * 7919) % 50000} "scriptc-bench/1.0"`,
|
||||
).join("\n") + "\n",
|
||||
);
|
||||
} else if (kind === "cli-config") {
|
||||
writeFileSync(path, JSON.stringify(deployManifest(), null, 2) + "\n");
|
||||
} else throw new Error(`unknown input kind ${kind}`);
|
||||
|
||||
@@ -17,9 +17,16 @@ const sanitize = process.env["SCRIPTC_SAN"] === "1";
|
||||
// SCRIPTC_NATIVE_PROFILE_GENERATE=<pattern> builds a compiler that writes a
|
||||
// raw profile per run (scripts/refresh-cli-profile.mjs). Optimized builds
|
||||
// otherwise use the committed profile, or the indexed profile that
|
||||
// SCRIPTC_NATIVE_PROFILE_USE names; an empty SCRIPTC_NATIVE_PROFILE_USE
|
||||
// builds without one.
|
||||
// SCRIPTC_NATIVE_PROFILE_USE names (gzip-compressed when the name ends in
|
||||
// .gz, as refresh-cli-profile.mjs writes it); an empty
|
||||
// SCRIPTC_NATIVE_PROFILE_USE builds without one.
|
||||
const profileGenerate = process.env["SCRIPTC_NATIVE_PROFILE_GENERATE"] || undefined;
|
||||
// Below this share of matching functions, the build warns that the profile
|
||||
// has lost most of its gain. A profile refreshed on the same commit matched
|
||||
// 92.7% of the native compiler's profiled functions, one refreshed 1.5 days
|
||||
// and about 200 commits earlier 87.2%, and that one kept about half of the
|
||||
// fresh profile's gain on a cold tsc-ts build.
|
||||
const PROFILE_MATCH_WARNING = 0.9;
|
||||
const committedProfile = join(root, "packages/compiler/native/cli.profdata.gz");
|
||||
const requestedProfile = process.env["SCRIPTC_NATIVE_PROFILE_USE"];
|
||||
const useProfile =
|
||||
@@ -45,10 +52,11 @@ const seed = join(dirname(output), ".scriptc", basename(output) + "-seed");
|
||||
for (const dir of [bin, lib, seed]) mkdirSync(dir, { recursive: true });
|
||||
let profileUse: string | undefined;
|
||||
if (useProfile) {
|
||||
profileUse = requestedProfile;
|
||||
if (profileUse === undefined) {
|
||||
profileUse = requestedProfile ?? committedProfile;
|
||||
if (profileUse.endsWith(".gz")) {
|
||||
const compressed = profileUse;
|
||||
profileUse = join(seed, "cli.profdata");
|
||||
writeFileSync(profileUse, gunzipSync(readFileSync(committedProfile)));
|
||||
writeFileSync(profileUse, gunzipSync(readFileSync(compressed)));
|
||||
}
|
||||
}
|
||||
const compiler = process.env["SCRIPTC_CC"] ?? target.defaultLinker;
|
||||
@@ -127,6 +135,25 @@ if (!result.ok)
|
||||
)
|
||||
.join("\n"),
|
||||
);
|
||||
// How well the profile still describes the compiler: a function whose
|
||||
// control flow changed since the profile was collected is optimized as
|
||||
// without one. The share falls as compiler code changes, and with it the
|
||||
// profile's gain (refresh it with scripts/refresh-cli-profile.mjs).
|
||||
const match = useProfile && "artifact" in result ? result.artifact.profileMatch : undefined;
|
||||
if (match !== undefined) {
|
||||
const profiled = match.annotated + match.mismatched;
|
||||
const share = profiled === 0 ? 0 : match.annotated / profiled;
|
||||
console.log(
|
||||
`native compiler profile: ${match.annotated} of ${profiled} profiled functions match (${(100 * share).toFixed(1)}%); ${match.missing} functions have no profile` +
|
||||
(match.unknownPartitions > 0
|
||||
? `; ${match.unknownPartitions} partitions reused without counts`
|
||||
: ""),
|
||||
);
|
||||
if (match.unknownPartitions === 0 && share < PROFILE_MATCH_WARNING)
|
||||
console.warn(
|
||||
`warning: the native compiler profile matches ${(100 * share).toFixed(1)}% of the functions it describes; run scripts/refresh-cli-profile.mjs`,
|
||||
);
|
||||
}
|
||||
const compilerVersion = (
|
||||
JSON.parse(readFileSync(join(root, "packages/compiler/package.json"), "utf8")) as {
|
||||
version: string;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env node
|
||||
/* Refreshes packages/compiler/native/cli.profdata.gz, the profile that release
|
||||
* builds of the native compiler optimize with (scripts/build-native-cli.mts).
|
||||
/* Refreshes packages/compiler/native/cli.profdata.gz, the profile that builds
|
||||
* of the native compiler optimize with (scripts/build-native-cli.mts). The
|
||||
* release workflow's cli-profile job runs it with --output on the release
|
||||
* commit, and release builds use that profile instead of the committed one.
|
||||
*
|
||||
* Builds an instrumented native compiler for this Linux host, runs it on the
|
||||
* training builds below, and merges the raw profiles it writes into one
|
||||
@@ -26,7 +28,7 @@ import {
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { cpus, tmpdir } from "node:os";
|
||||
import { join, resolve } from "node:path";
|
||||
import { dirname, join, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { parseArgs } from "node:util";
|
||||
import { constants, gzipSync } from "node:zlib";
|
||||
@@ -55,6 +57,9 @@ const tscTsPackage = join(tscTs, "packages/tsc-ts");
|
||||
if (!existsSync(join(tscTsPackage, "src/main.ts")))
|
||||
throw new Error(`${tscTs} is not a tsc-ts checkout with installed dependencies`);
|
||||
const jobs = Number(values.jobs);
|
||||
// The output's directory is created up front, before the instrumented build.
|
||||
const profileOutput = resolve(values.output);
|
||||
mkdirSync(dirname(profileOutput), { recursive: true });
|
||||
const work = resolve(values.work ?? mkdtempSync(join(tmpdir(), "scriptc-cli-profile-")));
|
||||
const raw = join(work, "raw");
|
||||
const builds = join(work, "builds");
|
||||
@@ -196,10 +201,12 @@ const merged = spawnSync(scriptc, ["merge-profiles", "--sparse", "-o", indexed,
|
||||
});
|
||||
if (merged.status !== 0) throw new Error("merge-profiles failed");
|
||||
// The indexed profile compresses to about an eighth of its size.
|
||||
const output = resolve(values.output);
|
||||
writeFileSync(output, gzipSync(readFileSync(indexed), { level: constants.Z_BEST_COMPRESSION }));
|
||||
writeFileSync(
|
||||
profileOutput,
|
||||
gzipSync(readFileSync(indexed), { level: constants.Z_BEST_COMPRESSION }),
|
||||
);
|
||||
console.log(
|
||||
`wrote ${output} (${statSync(output).size} bytes; ${statSync(indexed).size} uncompressed)`,
|
||||
`wrote ${profileOutput} (${statSync(profileOutput).size} bytes; ${statSync(indexed).size} uncompressed)`,
|
||||
);
|
||||
// A work directory the caller named stays for inspection.
|
||||
if (values.work === undefined) rmSync(work, { recursive: true, force: true });
|
||||
|
||||
@@ -395,7 +395,9 @@ export function dynamicTierSymbols(names) {
|
||||
* bench/channel-messages and bench/worker-messages, added later, link them
|
||||
* through worker messages:
|
||||
* node:worker_threads values are dyn objects. bench/json-unions, added
|
||||
* later too, decodes JSON.parse trees. bench/dynamic-methods and
|
||||
* later too, decodes JSON.parse trees. bench/throw-chains, also added
|
||||
* later, tests caught values with `instanceof`, as bench/validate-errors
|
||||
* does. bench/dynamic-methods and
|
||||
* bench/dynamic-properties are untyped JavaScript. Every other bench workload is fully typed and must link none. The gate and
|
||||
* tests/harness/bench-dynamic-tier.test.ts enforce that, and the test also
|
||||
* fails when a listed workload stops linking them, so remove a workload here
|
||||
@@ -416,7 +418,9 @@ export const DYNAMIC_WORKLOADS = new Set([
|
||||
"bench/typed-array-scan-workers",
|
||||
"bench/log-summary",
|
||||
"bench/inventory-report",
|
||||
"bench/line-reader",
|
||||
"bench/json-unions",
|
||||
"bench/throw-chains",
|
||||
]);
|
||||
|
||||
/** Names of the functions (text and weak symbols) in `nm` output. Mach-O
|
||||
|
||||
Reference in New Issue
Block a user