dart-sdk/benchmarks/EventLoopLatencyJson350KB/dart2/latency.dart
Martin Kustermann c3ddc5c374 [vm/concurrency] Add EventLoopLatencyJson350KB bechmark
The existing EventLoopLatencyJson benchmark exercises the *worst case scenario*:
A background isolate is stressing the GC where the working memory set
does not fit into new space. This causes scavenges to copy the entire
newspace either into the to-space or promote it to old space.

The benchmark in this CL is similar to EventLoopLatencyJson, but it's working
memory set is significantly lower than new space, therefore not
exercising this worst case scenario (this represents the most common
scenario, which we would expect for compute() function in flutter)

(Due to difficulty sharing code between benchmarks, this CL adds
similar code as EventLoopLatencyJson)

Issue https://github.com/dart-lang/sdk/issues/36097

Change-Id: I385a620ba8900082f8ca5fb9080fc23d95112e83
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/169100
Reviewed-by: Alexander Aprelev <aam@google.com>
Commit-Queue: Martin Kustermann <kustermann@google.com>
2020-10-28 16:28:38 +00:00

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);
}
}