blob: 96ebfc422af15a67bcdc1501d1f539e6122a901a [file] [log] [blame]
// Copyright (c) 2019, 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:isolate';
import 'dart:math';
import 'package:meta/meta.dart';
import 'package:compiler/src/dart2js.dart' as dart2js_main;
class SpawnLatency {
SpawnLatency(this.name);
Future<ResultMessageLatency> run() async {
final completerResult = Completer();
final receivePort = ReceivePort()..listen(completerResult.complete);
final isolateExitedCompleter = Completer<DateTime>();
final onExitReceivePort = ReceivePort()
..listen((_) {
isolateExitedCompleter.complete(DateTime.now());
});
final beforeSpawn = DateTime.now();
await Isolate.spawn(
isolateCompiler, StartMessageLatency(receivePort.sendPort, beforeSpawn),
onExit: onExitReceivePort.sendPort,
onError: onExitReceivePort.sendPort);
final afterSpawn = DateTime.now();
final ResultMessageLatency result = await completerResult.future;
receivePort.close();
final DateTime isolateExited = await isolateExitedCompleter.future;
result.timeToExitUs = isolateExited.difference(beforeSpawn).inMicroseconds;
result.timeToIsolateSpawnUs =
afterSpawn.difference(beforeSpawn).inMicroseconds;
onExitReceivePort.close();
return result;
}
Future<AggregatedResultMessageLatency> measureFor(int minimumMillis) async {
final minimumMicros = minimumMillis * 1000;
final watch = Stopwatch()..start();
final Metric toAfterIsolateSpawnUs = LatencyMetric('${name}ToAfterSpawn');
final Metric toStartRunningCodeUs = LatencyMetric('${name}ToStartRunning');
final Metric toFinishRunningCodeUs =
LatencyMetric('${name}ToFinishRunning');
final Metric toExitUs = LatencyMetric('${name}ToExit');
while (watch.elapsedMicroseconds < minimumMicros) {
final result = await run();
toAfterIsolateSpawnUs.add(result.timeToIsolateSpawnUs);
toStartRunningCodeUs.add(result.timeToStartRunningCodeUs);
toFinishRunningCodeUs.add(result.timeToFinishRunningCodeUs);
toExitUs.add(result.timeToExitUs);
}
return AggregatedResultMessageLatency(toAfterIsolateSpawnUs,
toStartRunningCodeUs, toFinishRunningCodeUs, toExitUs);
}
Future<AggregatedResultMessageLatency> measure() async {
await measureFor(500); // warm-up
return measureFor(4000); // actual measurement
}
Future<void> report() async {
final result = await measure();
print(result);
}
final String name;
RawReceivePort receivePort;
}
class Metric {
Metric({@required this.prefix, @required this.suffix});
void add(int value) {
if (value > max) {
max = value;
}
sum += value;
sumOfSquares += value * value;
count++;
}
double _average() => sum / count;
double _rms() => sqrt(sumOfSquares / count);
@override
String toString() => '$prefix): ${_average()}$suffix\n'
'${prefix}Max): $max$suffix\n'
'${prefix}RMS): ${_rms()}$suffix';
final String prefix;
final String suffix;
int max = 0;
double sum = 0;
double sumOfSquares = 0;
int count = 0;
}
class LatencyMetric extends Metric {
LatencyMetric(String name) : super(prefix: '$name(Latency', suffix: ' us.');
}
class StartMessageLatency {
StartMessageLatency(this.sendPort, this.spawned);
final SendPort sendPort;
final DateTime spawned;
}
class ResultMessageLatency {
ResultMessageLatency(
{this.timeToStartRunningCodeUs,
this.timeToFinishRunningCodeUs,
this.deltaHeap});
final int timeToStartRunningCodeUs;
final int timeToFinishRunningCodeUs;
final int deltaHeap;
int timeToIsolateSpawnUs;
int timeToExitUs;
}
class AggregatedResultMessageLatency {
AggregatedResultMessageLatency(
this.toAfterIsolateSpawnUs,
this.toStartRunningCodeUs,
this.toFinishRunningCodeUs,
this.toExitUs,
);
@override
String toString() => '''$toAfterIsolateSpawnUs
$toStartRunningCodeUs
$toFinishRunningCodeUs
$toExitUs''';
final Metric toAfterIsolateSpawnUs;
final Metric toStartRunningCodeUs;
final Metric toFinishRunningCodeUs;
final Metric toExitUs;
}
Future<void> isolateCompiler(StartMessageLatency start) async {
final timeRunningCodeUs = DateTime.now();
await runZoned(
() => dart2js_main.internalMain(<String>[
'benchmarks/IsolateSpawn/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
zoneSpecification: ZoneSpecification(
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}));
final timeFinishRunningCodeUs = DateTime.now();
start.sendPort.send(ResultMessageLatency(
timeToStartRunningCodeUs:
timeRunningCodeUs.difference(start.spawned).inMicroseconds,
timeToFinishRunningCodeUs:
timeFinishRunningCodeUs.difference(start.spawned).inMicroseconds));
}
Future<void> main() async {
await SpawnLatency('IsolateSpawn.Dart2JS').report();
}