Files
sdk/runtime/tests/vm/dart/transferable_test.dart
T
Alexander Aprelev 4ccae238ea [vm/isolate] Add TransferableTypedData class that allows low-cost passing of Uint8List between isolates.
TransferableTypedData instances are one-use kind of thing: once receiver materializes it, it can't be used
again, once sender sends it out to an isolate, sender can't send it to different isolate.

Example of use:

sender isolate:

```
Future<TransferableTypedData> consolidateHttpClientResponseBytes(HttpClientResponse response) {
  final completer = Completer<TransferableTypedData>();
  final chunks = <Uint8List>[];
  response.listen((List<int> chunk) {
    chunks.add(chunk);
  }, onDone: () {
    completer.complete(TransferableTypedData.fromList(chunks));
  });
  return completer.future;
}
...
sendPort.send(await consolidateHttpClientResponseBytes(response));
```

receiver isolate:
```
    RawReceivePort port = RawReceivePort((TransferableTypedData transferable) {
      Uint8List content = transferable.materialize().asUint8List();
      ...
    });
```

31959[tr] and 31960[tr] tests were inspired by dartbug.com/31959, dartbug.com/31960 that this CL attempts to address:
```
╰─➤  out/ReleaseX64/dart 31960.dart
sending...
163ms for round-trip
sending...
81ms for round-trip
sending...
20ms for round-trip
sending...
14ms for round-trip
sending...
20ms for round-trip
sending...
14ms for round-trip
```

(notice no "since last checking" pauses") vs

```
╰─➤  out/ReleaseX64/dart 31960.dart
sending...
154ms since last checkin
174ms for round-trip
sending...
68ms since last checkin
9ms since last checkin
171ms for round-trip
sending...
13ms since last checkin
108ms for round-trip
sending...
14ms since last checkin
108ms for round-trip
sending...
14ms since last checkin
107ms for round-trip
```

Change-Id: I0fcb5ce285394f498c3f1db4414204531f98199d
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99623
Commit-Queue: Alexander Aprelev <aam@google.com>
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Lasse R.H. Nielsen <lrn@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
2019-06-06 19:49:07 +00:00

131 lines
3.6 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.
// Test that validates that transferables are faster than regular typed data.
import 'dart:async';
import 'dart:isolate';
import 'dart:typed_data';
import "package:expect/expect.dart";
const int toIsolateSize = 100 * 1024 * 1024;
const int fromIsolateSize = 100 * 1024 * 1024;
const int nIterations = 5;
int iteration;
bool keepTimerRunning;
main() async {
keepTimerRunning = true;
print('--- standard');
iteration = nIterations;
final stopwatch = new Stopwatch()..start();
await runBatch(useTransferable: false);
final standard = stopwatch.elapsedMilliseconds;
print('--- transferable');
iteration = nIterations;
stopwatch.reset();
await runBatch(useTransferable: true);
final transferable = stopwatch.elapsedMilliseconds;
print(
'standard($standard ms)/transferable($transferable ms): ${standard / transferable}x');
Expect.isTrue(standard / transferable > 1.2);
keepTimerRunning = false;
}
packageList(Uint8List data, bool useTransferable) {
return useTransferable
? TransferableTypedData.fromList(<Uint8List>[data])
: data;
}
packageByteData(ByteData data, bool useTransferable) {
return useTransferable
? TransferableTypedData.fromList(<Uint8List>[data.buffer.asUint8List()])
: data;
}
class StartMessage {
final SendPort sendPort;
final bool useTransferable;
StartMessage(this.sendPort, this.useTransferable);
}
runBatch({bool useTransferable}) async {
Timer.run(idleTimer);
final port = ReceivePort();
final inbox = StreamIterator<dynamic>(port);
final worker = await Isolate.spawn(
isolateMain, StartMessage(port.sendPort, useTransferable),
paused: true);
final workerCompleted = Completer<bool>();
final workerExitedPort = ReceivePort()
..listen((_) => workerCompleted.complete(true));
worker.addOnExitListener(workerExitedPort.sendPort);
worker.resume(worker.pauseCapability);
await inbox.moveNext();
final outbox = inbox.current;
final workWatch = new Stopwatch();
final data = new Uint8List(toIsolateSize);
while (iteration-- > 0) {
final packagedData = packageList(data, useTransferable);
workWatch.start();
outbox.send(packagedData);
await inbox.moveNext();
final received = inbox.current;
final receivedData =
received is TransferableTypedData ? received.materialize() : received;
int time = workWatch.elapsedMilliseconds;
print('${time}ms for round-trip');
workWatch.reset();
}
outbox.send(null);
await workerCompleted.future;
workerExitedPort.close();
port.close();
}
Future<Null> isolateMain(StartMessage startMessage) async {
final port = new ReceivePort();
final inbox = new StreamIterator<dynamic>(port);
startMessage.sendPort.send(port.sendPort);
final data = Uint8List.view(new Uint8List(fromIsolateSize).buffer);
while (true) {
await inbox.moveNext();
final received = inbox.current;
if (received == null) {
break;
}
final receivedData =
received is TransferableTypedData ? received.materialize() : received;
final packagedData = packageList(data, startMessage.useTransferable);
startMessage.sendPort.send(packagedData);
}
port.close();
}
final Stopwatch idleWatch = new Stopwatch();
void idleTimer() {
idleWatch.stop();
final time = idleWatch.elapsedMilliseconds;
if (time > 5) print('${time}ms since last checkin');
idleWatch.reset();
idleWatch.start();
if (keepTimerRunning) {
Timer.run(idleTimer);
}
}