// 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:typed_data'; import 'package:benchmark_harness/benchmark_harness.dart' show PrintEmitter, ScoreEmitter; import 'package:meta/meta.dart'; class SendReceiveBytes extends AsyncBenchmarkBase { SendReceiveBytes(String name, {@required this.size, @required this.useTransferable}) : super(name); @override Future run() async { await helper.run(); } @override Future setup() async { helper = SendReceiveHelper(size, useTransferable: useTransferable); await helper.setup(); } @override Future teardown() async { await helper.finalize(); } final bool useTransferable; final int size; SendReceiveHelper helper; } // Identical to BenchmarkBase from package:benchmark_harness but async. abstract class AsyncBenchmarkBase { final String name; final ScoreEmitter emitter; Future run(); Future setup(); Future teardown(); const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()}); // Returns the number of microseconds per call. Future 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 measure() async { await setup(); await measureFor(500); // warm-up final result = await measureFor(4000); // actual measurement await teardown(); return result; } Future 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 setup() async { data = Uint8List(size); port = ReceivePort(); inbox = StreamIterator(port); workerCompleted = Completer(); 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 finalize() async { outbox.send(null); await workerCompleted.future; workerExitedPort.close(); port.close(); } // Send data to worker, wait for an answer. Future run() async { outbox.send(packageList(data, useTransferable)); await inbox.moveNext(); final received = inbox.current; if (useTransferable) { final TransferableTypedData transferable = received; transferable.materialize(); } } Uint8List data; ReceivePort port; StreamIterator inbox; SendPort outbox; Isolate worker; Completer workerCompleted; ReceivePort workerExitedPort; final int size; final bool useTransferable; } Object packageList(Uint8List data, bool useTransferable) => useTransferable ? TransferableTypedData.fromList([data]) : data; Future isolate(StartMessage startMessage) async { final port = ReceivePort(); final inbox = StreamIterator(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 sizes = [ 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 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(); } }