blob: b703340ce3ff0b8831eb03077c99f55eed812123 [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.
import 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart'
show PrintEmitter, ScoreEmitter;
class SendReceiveBytes extends AsyncBenchmarkBase {
SendReceiveBytes(String name,
{required this.size, required this.useTransferable})
: super(name);
@override
Future<void> run() async {
await helper.run();
}
@override
Future<void> setup() async {
helper = SendReceiveHelper(size, useTransferable: useTransferable);
await helper.setup();
}
@override
Future<void> teardown() async {
await helper.finalize();
}
final bool useTransferable;
final int size;
late SendReceiveHelper helper;
}
// Identical to BenchmarkBase from package:benchmark_harness but async.
abstract class AsyncBenchmarkBase {
final String name;
final ScoreEmitter emitter;
Future<void> run();
Future<void> setup();
Future<void> teardown();
const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
// Returns the number of microseconds per call.
Future<double> measureFor(int minimumMillis) async {
final minimumMicros = minimumMillis * 1000;
int iter = 0;
final watch = Stopwatch();
watch.start();
int elapsed = 0;
while (elapsed < minimumMicros) {
await run();
elapsed = watch.elapsedMicroseconds;
iter++;
}
return elapsed / iter;
}
// Measures the score for the benchmark and returns it.
Future<double> measure() async {
await setup();
await measureFor(500); // warm-up
final result = await measureFor(4000); // actual measurement
await teardown();
return result;
}
Future<void> report() async {
emitter.emit(name, await measure());
}
}
class StartMessage {
final SendPort sendPort;
final bool useTransferable;
final int size;
StartMessage(this.sendPort, this.useTransferable, this.size);
}
// Measures how long sending and receiving of [size]-length Uint8List takes.
class SendReceiveHelper {
SendReceiveHelper(this.size, {required this.useTransferable});
Future<void> setup() async {
data = Uint8List(size);
port = ReceivePort();
inbox = StreamIterator<dynamic>(port);
workerCompleted = Completer<bool>();
workerExitedPort = ReceivePort()
..listen((_) => workerCompleted.complete(true));
worker = await Isolate.spawn(
isolate, StartMessage(port.sendPort, useTransferable, size),
onExit: workerExitedPort.sendPort);
await inbox.moveNext();
outbox = inbox.current;
}
Future<void> finalize() async {
outbox.send(null);
await workerCompleted.future;
workerExitedPort.close();
port.close();
}
// Send data to worker, wait for an answer.
Future<void> run() async {
outbox.send(packageList(data, useTransferable));
await inbox.moveNext();
final received = inbox.current;
if (useTransferable) {
final TransferableTypedData transferable = received;
transferable.materialize();
}
}
late Uint8List data;
late ReceivePort port;
late StreamIterator<dynamic> inbox;
late SendPort outbox;
late Isolate worker;
late Completer<bool> workerCompleted;
late ReceivePort workerExitedPort;
final int size;
final bool useTransferable;
}
Object packageList(Uint8List data, bool useTransferable) =>
useTransferable ? TransferableTypedData.fromList(<Uint8List>[data]) : data;
Future<void> isolate(StartMessage startMessage) async {
final port = ReceivePort();
final inbox = StreamIterator<dynamic>(port);
final data = Uint8List.view(Uint8List(startMessage.size).buffer);
startMessage.sendPort.send(port.sendPort);
while (true) {
await inbox.moveNext();
final received = inbox.current;
if (received == null) {
break;
}
if (startMessage.useTransferable) {
final TransferableTypedData transferable = received;
transferable.materialize();
}
startMessage.sendPort.send(packageList(data, startMessage.useTransferable));
}
port.close();
}
class SizeName {
const SizeName(this.size, this.name);
final int size;
final String name;
}
const List<SizeName> sizes = <SizeName>[
SizeName(1 * 1024, '1KB'),
SizeName(10 * 1024, '10KB'),
SizeName(100 * 1024, '100KB'),
SizeName(1 * 1024 * 1024, '1MB'),
SizeName(10 * 1024 * 1024, '10MB'),
SizeName(100 * 1024 * 1024, '100MB')
];
Future<void> main() async {
for (final sizeName in sizes) {
await SendReceiveBytes('Isolate.SendReceiveBytes${sizeName.name}',
size: sizeName.size, useTransferable: false)
.report();
await SendReceiveBytes(
'Isolate.SendReceiveBytesTransferable${sizeName.name}',
size: sizeName.size,
useTransferable: true)
.report();
}
}