4ccae238ea
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>
131 lines
3.6 KiB
Dart
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);
|
|
}
|
|
}
|