Files
sdk/benchmarks/IsolateSpawn/dart/IsolateSpawn.dart
T
Ryan Macnak edaaa70e31 [benchmarks] Use gen_kernel instead of dart2js for isolate group benchmarks.
- dart2js does not run in NNBD but gen_kernel does.
 - Changes to dart2js to not trigger VM benchmarks to run on Golem, allowing performance differences to accumulate until the next unrelated VM change. Changes to the front end or package:vm cause a binary diff in the platform file or kernel-service snapshot and so do trigger VM benchmarks to run on Golem.
 - These benchmarks don't care about running dart2js in particular, just some large program.

Change-Id: I85e339c32edbf9d0261081623378475fa0b9507a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/270022
Reviewed-by: Siva Annamalai <asiva@google.com>
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Jonas Termansen <sortie@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
2022-11-21 22:25:59 +00:00

164 lines
4.9 KiB
Dart

// 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.
import 'dart:async';
import 'dart:isolate';
import 'dart:math';
import '../../../pkg/vm/bin/gen_kernel.dart' as gen_kernel;
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;
late 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(
{required this.timeToStartRunningCodeUs,
required this.timeToFinishRunningCodeUs});
final int timeToStartRunningCodeUs;
final int timeToFinishRunningCodeUs;
late int timeToIsolateSpawnUs;
late 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(
() => gen_kernel.compile(<String>[
'benchmarks/IsolateSpawn/dart/helloworld.dart',
'benchmarks/IsolateSpawn/dart/helloworld.dart.dill',
]),
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();
}