blob: c0b44486d046c525b5fe85a6d89563c58dec7705 [file] [log] [blame]
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import "dart:io";
import "benchmarker.dart" as benchmarker;
const String compileDartRelativePath = "pkg/front_end/tool/_fasta/compile.dart";
const int iterations = 3;
String? target;
String? changingWorkingDir;
String? sdkPath;
String? snapshotsPath;
void main(List<String> args) {
if (args.contains("--help")) return _help();
bool filter = false;
bool raw = false;
List<String> examines = [];
List<String> extraVmArguments = [];
for (String arg in args) {
if (arg.startsWith("--target=")) {
target = arg.substring("--target=".length);
} else if (arg.startsWith("--changingWorkingDir=")) {
changingWorkingDir = arg.substring("--changingWorkingDir=".length);
} else if (arg.startsWith("--sdkPath=")) {
sdkPath = arg.substring("--sdkPath=".length);
} else if (arg.startsWith("--snapshotsPath=")) {
snapshotsPath = arg.substring("--snapshotsPath=".length);
} else if (arg == "--filter") {
filter = true;
} else if (arg == "--raw") {
raw = true;
} else if (arg.startsWith("--examine=")) {
examines.addAll(arg.substring("--examine=".length).split(","));
} else if (arg.startsWith("--extraVmArguments=")) {
// E.g. "--old_gen_growth_rate=1000" in an attempt to even out GC stuff.
extraVmArguments.add(arg.substring("--extraVmArguments=".length));
} else {
throw "Unknown argument: $arg";
}
}
if (target == null) throw "Specify --target";
if (changingWorkingDir == null) throw "Specify --changingWorkingDir";
if (snapshotsPath == null) throw "Specify --snapshotsPath";
if (sdkPath == null) throw "Specify --sdkPath";
if (!new File("${sdkPath}bin/dart").existsSync()) {
throw "--sdkPath doesn't contain bin/dart";
}
if (!new File("${sdkPath}bin/dartaotruntime").existsSync()) {
throw "--sdkPath doesn't contain bin/dartaotruntime";
}
if (examines.isEmpty) {
throw "Specify one or more commits to examine via --examine=";
}
for (String examine in examines) {
if (examine.trim() == "") continue;
_examine(examine, filter, raw, extraVmArguments);
}
}
void _help() {
print("CFE revision benchmarking tool");
print("");
print("Specify target, i.e. what we'll compile when benchmarking via");
print("--target=<dart file>");
print("Specify a git checkout that this script can manage");
print("(and delete untracked files etc in) via");
print("--changingWorkingDir=<checkout dir>");
print("Specify the sdk to use via");
print("--sdkPath=<path>");
print("Specify where to save the snapshots via");
print("--snapshotsPath=<path>");
print("Specify which commit(s) to examine with");
print("--examine=<revision>");
print("(specify more either with more --examine= arguments,");
print("or by comma-separation)");
print("");
print("Control output as needed with");
print("--filter");
print("and");
print("--raw");
print("");
print("Specify extra arguments to pass to the VM with");
print("--extraVmArguments=<whatever>");
print("E.g. `--extraVmArguments=--old_gen_growth_rate=1000`");
print("");
print("Example run:");
print("");
print(r"out/ReleaseX64/dart-sdk/bin/dart \");
print(r" pkg/front_end/tool/compare_revisions_tool.dart \");
print(r" --target=pkg/front_end/tool/_fasta/compile.dart \");
print(r" --changingWorkingDir=/tmp/tmp-playing-with-git/sdk/ \");
print(r" --sdkPath=67e9580b042/dart-sdk/ \");
print(r" --snapshotsPath=67e9580b042 \");
print(r" --examine=e7deece1fb2,529e016a0a7,fd02fec0fc4 \");
}
void _compileRevision(String gitCommit, {Stopwatch? stopwatch}) {
if (new File("$snapshotsPath/platform.dill.$gitCommit").existsSync() &&
new File("$snapshotsPath/compile.aot.$gitCommit").existsSync()) {
return;
}
stopwatch ??= new Stopwatch()..start();
// Clean up the git checkout.
ProcessResult processResult = Process.runSync("git", ["reset", "--hard"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed resetting hard for $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
processResult = Process.runSync("git", ["clean", "-d", "-f"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed cleaning for $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
// Checkout at the specific revision.
processResult = Process.runSync("git", ["checkout", gitCommit],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed checking out $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
print("Done running git checkout for $gitCommit after "
"${stopwatch.elapsed.inSeconds} seconds.");
// Clean up the git checkout.
processResult = Process.runSync("git", ["reset", "--hard"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed resetting hard for $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
processResult = Process.runSync("git", ["clean", "-d", "-f"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed cleaning for $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
// Run `gclient sync`.
processResult = Process.runSync("gclient", ["sync", "-D"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed gclient sync at $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
print("Done running `gclient sync` for $gitCommit "
"after ${stopwatch.elapsed.inSeconds} seconds.");
// Build the platform and copy it so we have it.
processResult = Process.runSync(
"python3", ["tools/build.py", "-ax64", "-mrelease", "vm_platform"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed compile vm platform at $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
new File("$changingWorkingDir/out/ReleaseX64/vm_platform_strong.dill")
.copySync("$snapshotsPath/platform.dill.$gitCommit");
print("Done building the platform for $gitCommit "
"after ${stopwatch.elapsed.inSeconds} seconds.");
// Compile the AOT snapshot.
processResult = Process.runSync("${sdkPath}bin/dart", [
"compile",
"aot-snapshot",
"$changingWorkingDir/$compileDartRelativePath",
"-o",
"$snapshotsPath/compile.aot.$gitCommit"
]);
if (processResult.exitCode != 0) {
throw "Failed compile aot-snapshot at $gitCommit:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
print("Compiled for $gitCommit "
"after ${stopwatch.elapsed.inSeconds} seconds.");
}
void _examine(
String revision, bool filter, bool raw, List<String> extraVmArguments) {
ProcessResult processResult = Process.runSync(
"git", ["log", "$revision^^...$revision", "--pretty=format:%h"],
workingDirectory: changingWorkingDir);
if (processResult.exitCode != 0) {
throw "Failed to get log:\n"
"stderr: ${processResult.stderr}\n"
"stdout: ${processResult.stdout}";
}
List<String> revisions = processResult.stdout.split("\n").reversed.toList();
if (revisions.length != 2) throw "Expected 2 revisions but got $revisions";
if (revisions.last != revision) {
throw "Expected $revision but got ${revisions.last}";
}
String prevRevision = revisions.first;
print("Creating AOT-snapshots if needed.");
_compileRevision(prevRevision);
_compileRevision(revision);
print("Will now examine $prevRevision -> $revision.");
print("Running with verbose GC.");
benchmarker.GCInfo gcPrev = _runVerboseGc(prevRevision, extraVmArguments);
benchmarker.GCInfo gcCurrent = _runVerboseGc(revision, extraVmArguments);
benchmarker.printGcDiff(gcPrev, gcCurrent);
print("Running $iterations iterations for $prevRevision.");
List<Map<String, num>> benchmarkDataFrom = [];
_run(iterations, prevRevision, extraVmArguments, benchmarkDataFrom);
print("Running $iterations iterations for $revision.");
List<Map<String, num>> benchmarkDataTo = [];
_run(iterations, revision, extraVmArguments, benchmarkDataTo);
if (filter) {
// Filter to only "instructions:u".
benchmarkDataFrom = _filterToInstructions(benchmarkDataFrom);
benchmarkDataTo = _filterToInstructions(benchmarkDataTo);
}
if (raw) {
print("From: $benchmarkDataFrom");
print("To: $benchmarkDataTo");
}
print("Examine: $prevRevision -> $revision:");
if (!benchmarker.compare(benchmarkDataFrom, benchmarkDataTo)) {
print("No change.");
}
}
void _run(int iterations, String gitCommit, List<String> extraVmArguments,
List<Map<String, num>> output) {
for (int i = 0; i < iterations; i++) {
try {
output.add(benchmarker.benchmark(
"$snapshotsPath/compile.aot.$gitCommit",
extraVmArguments,
[
"--platform=$snapshotsPath/platform.dill.$gitCommit",
target!,
],
aotRuntime: "${sdkPath}bin/dartaotruntime"));
} catch (e) {
throw "Failed to run benchmark at $gitCommit";
}
}
}
benchmarker.GCInfo _runVerboseGc(
String gitCommit, List<String> extraVmArguments) {
ProcessResult processResult =
Process.runSync("${sdkPath}bin/dartaotruntime", [
"--deterministic",
"--verbose-gc",
...extraVmArguments,
"$snapshotsPath/compile.aot.$gitCommit",
"--platform=$snapshotsPath/platform.dill.$gitCommit",
target!
]);
if (processResult.exitCode != 0) {
throw "Run failed for $gitCommit with exit code "
"${processResult.exitCode}.\n"
"stdout:\n${processResult.stdout}\n\n"
"stderr:\n${processResult.stderr}\n\n";
}
return benchmarker.parseVerboseGcOutput(processResult);
}
List<Map<String, num>> _filterToInstructions(List<Map<String, num>> input,
{List<num>? extractedNumbers}) {
List<Map<String, num>> result = [];
for (Map<String, num> map in input) {
num? instructionsValue = map["instructions:u"];
if (instructionsValue != null) {
result.add({"instructions:u": instructionsValue});
extractedNumbers?.add(instructionsValue);
}
}
return result;
}