mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:49:17 +00:00
c7085f211f
Change-Id: If1ec76839f5334d8a1e04a298b19a4211502fdeb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/168830 Commit-Queue: Jonas Termansen <sortie@google.com> Reviewed-by: Jonas Termansen <sortie@google.com>
138 lines
4.2 KiB
Dart
138 lines
4.2 KiB
Dart
// Copyright (c) 2020, 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.
|
|
|
|
// @dart=2.9
|
|
|
|
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:math' as math;
|
|
import 'dart:typed_data';
|
|
|
|
/// Measures event loop responsiveness.
|
|
///
|
|
/// Schedules new timer events, [tickDuration] in the future, and measures how
|
|
/// long it takes for these events to actually arrive.
|
|
///
|
|
/// Runs [numberOfTicks] times before completing with [EventLoopLatencyStats].
|
|
Future<EventLoopLatencyStats> measureEventLoopLatency(
|
|
Duration tickDuration, int numberOfTicks) {
|
|
final completer = Completer<EventLoopLatencyStats>();
|
|
|
|
final tickDurationInUs = tickDuration.inMicroseconds;
|
|
final buffer = _TickLatencies(numberOfTicks);
|
|
final sw = Stopwatch()..start();
|
|
int lastTimestamp = 0;
|
|
|
|
void trigger() {
|
|
final int currentTimestamp = sw.elapsedMicroseconds;
|
|
|
|
// Every tick we missed to schedule we'll add with difference to when we
|
|
// would've scheduled it and when we became responsive again.
|
|
bool done = false;
|
|
while (!done && lastTimestamp < (currentTimestamp - tickDurationInUs)) {
|
|
done = !buffer.add(currentTimestamp - lastTimestamp - tickDurationInUs);
|
|
lastTimestamp += tickDurationInUs;
|
|
}
|
|
|
|
if (!done) {
|
|
lastTimestamp = currentTimestamp;
|
|
Timer(tickDuration, trigger);
|
|
} else {
|
|
completer.complete(buffer.makeStats());
|
|
}
|
|
}
|
|
|
|
Timer(tickDuration, trigger);
|
|
|
|
return completer.future;
|
|
}
|
|
|
|
/// Result of the event loop latency measurement.
|
|
class EventLoopLatencyStats {
|
|
/// Minimum latency between scheduling a tick and it's arrival (in ms).
|
|
final double minLatency;
|
|
|
|
/// Average latency between scheduling a tick and it's arrival (in ms).
|
|
final double avgLatency;
|
|
|
|
/// Maximum latency between scheduling a tick and it's arrival (in ms).
|
|
final double maxLatency;
|
|
|
|
/// The 50th percentile (median) (in ms).
|
|
final double percentile50th;
|
|
|
|
/// The 90th percentile (in ms).
|
|
final double percentile90th;
|
|
|
|
/// The 95th percentile (in ms).
|
|
final double percentile95th;
|
|
|
|
/// The 99th percentile (in ms).
|
|
final double percentile99th;
|
|
|
|
/// The maximum RSS of the process.
|
|
final int maxRss;
|
|
|
|
EventLoopLatencyStats(
|
|
this.minLatency,
|
|
this.avgLatency,
|
|
this.maxLatency,
|
|
this.percentile50th,
|
|
this.percentile90th,
|
|
this.percentile95th,
|
|
this.percentile99th,
|
|
this.maxRss);
|
|
|
|
void report(String name) {
|
|
print('$name.Min(RunTimeRaw): $minLatency ms.');
|
|
print('$name.Avg(RunTimeRaw): $avgLatency ms.');
|
|
print('$name.Percentile50(RunTimeRaw): $percentile50th ms.');
|
|
print('$name.Percentile90(RunTimeRaw): $percentile90th ms.');
|
|
print('$name.Percentile95(RunTimeRaw): $percentile95th ms.');
|
|
print('$name.Percentile99(RunTimeRaw): $percentile99th ms.');
|
|
print('$name.Max(RunTimeRaw): $maxLatency ms.');
|
|
print('$name.MaxRss(MemoryUse): $maxRss');
|
|
}
|
|
}
|
|
|
|
/// Accumulates tick latencies and makes statistics for it.
|
|
class _TickLatencies {
|
|
final Uint64List _timestamps;
|
|
int _index = 0;
|
|
|
|
_TickLatencies(int numberOfTicks) : _timestamps = Uint64List(numberOfTicks);
|
|
|
|
/// Returns `true` while the buffer has not been filled yet.
|
|
bool add(int latencyInUs) {
|
|
_timestamps[_index++] = latencyInUs;
|
|
return _index < _timestamps.length;
|
|
}
|
|
|
|
EventLoopLatencyStats makeStats() {
|
|
if (_index != _timestamps.length) {
|
|
throw 'Buffer has not been fully filled yet.';
|
|
}
|
|
|
|
_timestamps.sort();
|
|
final length = _timestamps.length;
|
|
final double avg = _timestamps.fold(0, (int a, int b) => a + b) / length;
|
|
final int min = _timestamps.fold(0x7fffffffffffffff, math.min);
|
|
final int max = _timestamps.fold(0, math.max);
|
|
final percentile50th = _timestamps[50 * length ~/ 100];
|
|
final percentile90th = _timestamps[90 * length ~/ 100];
|
|
final percentile95th = _timestamps[95 * length ~/ 100];
|
|
final percentile99th = _timestamps[99 * length ~/ 100];
|
|
|
|
return EventLoopLatencyStats(
|
|
min / 1000,
|
|
avg / 1000,
|
|
max / 1000,
|
|
percentile50th / 1000,
|
|
percentile90th / 1000,
|
|
percentile95th / 1000,
|
|
percentile99th / 1000,
|
|
ProcessInfo.maxRss);
|
|
}
|
|
}
|