mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 12:24:24 +00:00
fc2fcf9bc8
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 .
109 lines
3.7 KiB
Dart
109 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');
|
|
}
|
|
}
|