mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 02:07:06 +00:00
Tool to compute BMU plots from GC logs.
R=iposva@google.com Review URL: https://codereview.chromium.org//1133243004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@45786 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
47534318f6
commit
9b4219332b
38
runtime/tools/bmu_benchmark_gallery.sh
Normal file
38
runtime/tools/bmu_benchmark_gallery.sh
Normal file
|
@ -0,0 +1,38 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2014, 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.
|
||||
|
||||
# Wrapper that runs a given Dart VM over the benchmarks with --verbose_gc
|
||||
# and uses the verbose_gc_to_bmu script to produce a gallery of BMU graphs.
|
||||
|
||||
if [ "$#" -ne 3 ]
|
||||
then
|
||||
echo "Usage: $0 dart_binary benchmark_directory output_directory"
|
||||
echo "Example: $0 out/ReleaseIA32/dart ../golem4/benchmarks /tmp/bmu"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DART_BIN=$1
|
||||
BENCH_DIR=$2
|
||||
OUT_DIR=$3
|
||||
|
||||
VERBOSE_GC_TO_BMU=$(dirname "$0")/verbose_gc_to_bmu.dart
|
||||
INDEX_FILE=$OUT_DIR/index.html
|
||||
TMP=/tmp/bmu_benchmark_gallery
|
||||
|
||||
mkdir -p $OUT_DIR
|
||||
echo "<html><body>" > $INDEX_FILE
|
||||
$DART_BIN --version 2>> $INDEX_FILE
|
||||
echo "<br>" >> $INDEX_FILE
|
||||
for NAME in `ls $BENCH_DIR`
|
||||
do
|
||||
$DART_BIN --verbose_gc $BENCH_DIR/$NAME/dart/$NAME.dart 2> $TMP.gclog &&
|
||||
$DART_BIN $VERBOSE_GC_TO_BMU < $TMP.gclog > $TMP.dat &&
|
||||
gnuplot -e "set term png; set output '$TMP.png'; set title '$NAME'; set ylabel 'BMU'; set xlabel 'Window size (ms)'; unset key; set yr [0:1]; set logscale x; plot '$TMP.dat' with linespoints" &&
|
||||
mv -f $TMP.png $OUT_DIR/$NAME.png &&
|
||||
mv -f $TMP.gclog $OUT_DIR/$NAME.txt &&
|
||||
echo "<a href='$NAME.txt'><img src='$NAME.png'></a>" >> $INDEX_FILE
|
||||
done
|
||||
echo "</body></html>" >> $INDEX_FILE
|
105
runtime/tools/verbose_gc_to_bmu.dart
Normal file
105
runtime/tools/verbose_gc_to_bmu.dart
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
// Tool to compute bounded mutator utilization (BMU) from a --verbose_gc log.
|
||||
// Outputs CSV suitable for, e.g., gnuplot:
|
||||
//
|
||||
// dart --verbose_gc foo.dart 2> foo.gclog
|
||||
// dart verbose_gc_to_bmu.dart < foo.gclog > foo.bmu
|
||||
// gnuplot -p -e "set yr [0:1]; set logscale x; plot 'foo.bmu' with linespoints"
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
const WINDOW_STEP_FACTOR = 0.9;
|
||||
const MINIMUM_WINDOW_SIZE_MS = 1;
|
||||
|
||||
class Interval<T> {
|
||||
T begin;
|
||||
T end;
|
||||
Interval(this.begin, this.end);
|
||||
T get length => max(0, end - begin);
|
||||
Interval<T> overlap(Interval<T> other) =>
|
||||
new Interval(max(this.begin, other.begin), min(this.end, other.end));
|
||||
}
|
||||
|
||||
class Timeline {
|
||||
// Pauses must be added in non-decreasing order of 'begin'.
|
||||
void addPause(Interval<int> pause) {
|
||||
var last = _pauses.isEmpty ? new Interval<int>(0, 0) : _pauses.last;
|
||||
assert(last.begin <= pause.begin);
|
||||
// Trim any initial overlap.
|
||||
_pauses.add(new Interval(max(pause.begin, last.end), pause.end));
|
||||
// TODO(koda): Make VM log actual end time, rather than just last GC end.
|
||||
_run.end = max(_run.end, pause.end);
|
||||
}
|
||||
|
||||
int get maxWindowSize => _run.length;
|
||||
|
||||
// The windowSize must be no larger than the entire run.
|
||||
double minUtilization(int windowSize) {
|
||||
assert(windowSize <= _run.length);
|
||||
// The minimum utilization can always be found in a window that has one of
|
||||
// its endpoints at the beginning or end of a pause or the entire timeline.
|
||||
List<int> interesting = [_run.begin, _run.end];
|
||||
for (Interval p in _pauses) {
|
||||
interesting.add(p.begin);
|
||||
interesting.add(p.end);
|
||||
}
|
||||
double result = 1.0;
|
||||
for (int i in interesting) {
|
||||
result = min(result, _utilization(new Interval(i, i + windowSize)));
|
||||
result = min(result, _utilization(new Interval(i - windowSize, i)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Returns the fraction of non-pause time, or 1.0 for an invalid interval.
|
||||
double _utilization(Interval<int> iv) {
|
||||
if (_run.begin > iv.begin || iv.end > _run.end || iv.length == 0) {
|
||||
return 1.0;
|
||||
}
|
||||
int paused = 0;
|
||||
for (Interval<int> p in _pauses) {
|
||||
paused += p.overlap(iv).length;
|
||||
}
|
||||
return 1.0 - (paused / iv.length);
|
||||
}
|
||||
|
||||
final Interval<int> _run = new Interval<int>(0, 0);
|
||||
final List<Interval<int>> _pauses = [];
|
||||
}
|
||||
|
||||
// Returns a GC pause as an interval in microseconds since program start, or
|
||||
// the interval [0, 0) on parse error.
|
||||
Interval<int> parseVerboseGCLine(String line) {
|
||||
var fields = line.split(',');
|
||||
// Update this (and indices below, if needed) when logging format changes.
|
||||
if (fields.length != 25) {
|
||||
assert(line.startsWith('[ GC | space | count | start | gc time') ||
|
||||
line.startsWith('[ (isolate)| (reason)| | (s) | (ms) '));
|
||||
return new Interval<int>(0, 0);
|
||||
}
|
||||
var begin = (1e6 * double.parse(fields[2])).floor();
|
||||
var duration = (1000 * double.parse(fields[3])).floor();
|
||||
var end = begin + duration;
|
||||
return new Interval<int>(begin, end);
|
||||
}
|
||||
|
||||
void main() {
|
||||
Timeline t = new Timeline();
|
||||
for (String line = stdin.readLineSync();
|
||||
line != null;
|
||||
line = stdin.readLineSync()) {
|
||||
t.addPause(parseVerboseGCLine(line));
|
||||
}
|
||||
print('# window_size_ms, bounded_mutator_utilization');
|
||||
var minimumSeen = 1.0;
|
||||
for (int w = t._run.length;
|
||||
w > 1000 * MINIMUM_WINDOW_SIZE_MS;
|
||||
w = (w * WINDOW_STEP_FACTOR).floor()) {
|
||||
minimumSeen = min(minimumSeen, t.minUtilization(w));
|
||||
print('${w / 1000}, $minimumSeen');
|
||||
}
|
||||
}
|
71
tests/standalone/verbose_gc_to_bmu_test.dart
Normal file
71
tests/standalone/verbose_gc_to_bmu_test.dart
Normal file
|
@ -0,0 +1,71 @@
|
|||
// Copyright (c) 2015, 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.
|
||||
|
||||
// This test forks a second vm process that runs the BMU tool and verifies that
|
||||
// it produces some output. This test is mainly here to ensure that the BMU
|
||||
// tool compiles and runs.
|
||||
|
||||
import "dart:async";
|
||||
import "dart:convert";
|
||||
import "dart:io";
|
||||
|
||||
import "package:path/path.dart";
|
||||
|
||||
// Tool script relative to the path of this test.
|
||||
var toolScript = "../../runtime/tools/verbose_gc_to_bmu.dart";
|
||||
|
||||
// Target script relative to this test.
|
||||
var targetScript = "../language/gc_test.dart";
|
||||
const minOutputLines = 20;
|
||||
|
||||
void checkExitCode(exitCode) {
|
||||
if (exitCode != 0) {
|
||||
print("Process terminated with exit code ${exitCode}.");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// Compute paths for tool and target relative to the path of this script.
|
||||
var scriptDir = dirname(Platform.script.toFilePath());
|
||||
var targPath = normalize(join(scriptDir, targetScript));
|
||||
var targetResult =
|
||||
Process.runSync(Platform.executable, ["--verbose_gc", targPath]);
|
||||
checkExitCode(targetResult.exitCode);
|
||||
var gcLog = targetResult.stderr;
|
||||
var toolPath = normalize(join(scriptDir, toolScript));
|
||||
Process.start(Platform.executable, [toolPath]).then((Process process) {
|
||||
// Feed the GC log of the target to the BMU tool.
|
||||
process.stdin.write(gcLog);
|
||||
process.stdin.close();
|
||||
var stdoutStringStream = process.stdout
|
||||
.transform(UTF8.decoder)
|
||||
.transform(new LineSplitter());
|
||||
var stderrStringStream = process.stderr
|
||||
.transform(UTF8.decoder)
|
||||
.transform(new LineSplitter());
|
||||
// Wait for 3 future events: stdout and stderr streams closed, and
|
||||
// process terminated.
|
||||
var futures = [];
|
||||
var stdoutLines = [];
|
||||
var stderrLines = [];
|
||||
var subscription = stdoutStringStream.listen(stdoutLines.add);
|
||||
futures.add(subscription.asFuture(true));
|
||||
subscription = stderrStringStream.listen(stderrLines.add);
|
||||
futures.add(subscription.asFuture(true));
|
||||
futures.add(process.exitCode.then(checkExitCode));
|
||||
Future.wait(futures).then((results) {
|
||||
if (stderrLines.isNotEmpty) {
|
||||
print("Unexpected output on stderr:");
|
||||
print(stderrLines.join('\n'));
|
||||
exit(-1);
|
||||
}
|
||||
if (stdoutLines.length < minOutputLines) {
|
||||
print("Less than expected output on stdout:");
|
||||
print(stdoutLines.join('\n'));
|
||||
exit(-1);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue