Файли
openidea/perf/rust.ts
T
l0ng-ai fd1f465aeb feat(plugins): extension points, capability consent, async runtime and plugin API 1.0
Rework the plugin system per docs/plugin_plan.md (P0–P8):

- Extension-point registry (ide-extension) so new contribution types register
  without touching the kernel; languages, run detectors, providers, views and
  UI contributions go through it.
- Per-plugin capability consent with a risk-graded ledger; process
  capabilities are pinned to resolved binary paths and hashes.
- Async, actor-per-plugin Wasm runtime with lazy activation, cancellation,
  events, long-running tasks and idle reclamation.
- Plugin host moved into ide-host, shared by local and remote projects.
- Distribution: install/package tooling (openidea-plugin bin) and a static
  market index.
- WIT frozen as openidea:[email protected]; Rust SDK with mock host.
- Ten official validation plugins under plugins/official.
2026-11-04 02:41:46 +08:00

86 рядки
3.8 KiB
TypeScript
Неформатований Анотація Історія

Цей файл містить неоднозначні символи Юнікоду
Цей файл містить символи Юнікоду, які можна сплутати з іншими символами. Якщо ви вважаєте, що це зроблено навмисно, можете сміливо ігнорувати це попередження. Щоб показати їх, скористайтеся кнопкою Escape.
/**
* Backend performance gate: runs the criterion benches of the crates on the
* interactive path and checks their means (µs) against `budgets.json` › rust
* and the local baseline.
*
* pnpm perf:rust run benches, then check
* pnpm perf:rust -- --no-run check the latest criterion results only
* pnpm perf:rust -- --package ide-text --save-baseline
*/
import { spawnSync } from "node:child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { parseArgs } from "node:util";
import { evaluate, loadBudgets, printReport, type ScenarioResult } from "./lib/report.ts";
const ROOT = resolve(import.meta.dirname, "..");
const TARGET = resolve(ROOT, process.env.CARGO_TARGET_DIR ?? "target");
const OUT = join(ROOT, "target", "perf");
const PACKAGES = ["ide-text", "ide-language", "ide-index", "ide-search", "ide-host"];
const { values: args } = parseArgs({
options: {
package: { type: "string", multiple: true },
"no-run": { type: "boolean", default: false },
"save-baseline": { type: "boolean", default: false },
"fail-on-regression": { type: "boolean", default: false },
},
});
const packages = args.package ?? PACKAGES;
const started = Date.now();
if (!args["no-run"]) {
const r = spawnSync("cargo", ["bench", ...packages.flatMap((p) => ["-p", p]), "--benches"], {
cwd: ROOT,
stdio: "inherit",
});
if (r.status !== 0) throw new Error("cargo bench failed");
}
/** Criterion results (`<id>/new/{benchmark,estimates}.json`) → mean in µs. */
function collect(dir: string, out: Record<string, number>) {
if (!existsSync(dir)) return;
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (!entry.isDirectory() || entry.name === "report") continue;
const sub = join(dir, entry.name);
const bench = join(sub, "new", "benchmark.json");
if (existsSync(bench) && (args["no-run"] || statSync(bench).mtimeMs >= started)) {
const { full_id } = JSON.parse(readFileSync(bench, "utf8")) as { full_id: string };
const est = JSON.parse(readFileSync(join(sub, "new", "estimates.json"), "utf8")) as {
mean: { point_estimate: number };
};
out[full_id] = est.mean.point_estimate / 1000;
}
collect(sub, out);
}
}
const metrics: Record<string, number> = {};
collect(join(TARGET, "criterion"), metrics);
if (Object.keys(metrics).length === 0) throw new Error(`no criterion results under ${join(TARGET, "criterion")}`);
mkdirSync(OUT, { recursive: true });
const budgets = loadBudgets(join(ROOT, "perf", "budgets.json"));
const results: ScenarioResult[] = [{ id: "rust", title: "criterion means (µs)", metrics }];
const baselineFile = join(OUT, "baseline-rust.json");
const baseline = existsSync(baselineFile) ? (JSON.parse(readFileSync(baselineFile, "utf8")) as ScenarioResult[]) : null;
// Only budgets of benches that ran: `--package` may select a subset.
const rustBudgets = Object.fromEntries(
Object.entries(budgets.rust)
.filter(([id]) => args["no-run"] || id in metrics || !args.package)
.map(([id, v]) => [`rust.${id}`, v]),
);
const report = evaluate(results, rustBudgets, baseline, {
enforceBudgets: true,
tolerance: budgets.regressionTolerance,
floor: budgets.regressionFloor,
});
const meta = { date: new Date().toISOString(), unit: "µs (mean)", packages: packages.join(" ") };
writeFileSync(join(OUT, "report-rust.json"), JSON.stringify({ meta, ...report }, null, 2));
writeFileSync(join(OUT, "report-rust.md"), printReport(report, meta));
if (args["save-baseline"]) {
writeFileSync(baselineFile, JSON.stringify(results, null, 2));
console.log(`Baseline saved to ${baselineFile}`);
}
process.exit(report.failures > 0 || (args["fail-on-regression"] && report.regressions > 0) ? 1 : 0);