dart-sdk/runtime/tools/verbose_gc_to_bmu.dart
Erik Corry fc2fcf9bc8 Add more safe points in compiler.
The Scavenge (young-gen) GCs on the main thread have to wait for other
threads to check in at a safe point. We were seeing big waits here, often
20ms, occasionally up to 180ms where the main thread is idling, waiting
for the optimizing compiler.  By adding more safe points the wait is
reduced and is now rarely over 10ms, often under 1ms.

This also changes the --verbose-gc output to be better aligned with the
column headings, and to add the time needed to get to
the safe point to the output, eg:

[ GC(784211551): Scavenge(new space), 18, 2.209, 76.009, 32768, 0, 32768, 32768, 0, 0, 144912, 154425, 152064, 154880, 0, 0, 46.984, 2.752, 7.407, 18.657, 0.033, 5421, 0, 0, 0, ]
                                                 ^^^^^^ Scavenge time                                                        ^^^^^^  safe point time.

R=vegorov@google.com
BUG=

Review-Url: https://codereview.chromium.org/2771013002 .
2017-03-31 11:19:46 +02:00

110 lines
3.7 KiB
Dart

// 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.
//
// NOTE: See also wrapper script sdk/runtime/tools/bmu_benchmark_gallery.sh
//
// 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 < 10) {
// Ignore the lines that just specify column names, separated by '|'.
// We assume these have very few commas in them, so that fields.length
// is < 10.
assert(line.contains("|"));
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');
}
}