blob: 01cc96924782af13217327b41ccd173b1ebdc7fd [file] [log] [blame]
// Copyright (c) 2022, 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:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
void main() async {
for (var serializationMode in [
SerializationMode.jsonClient,
SerializationMode.byteDataClient
]) {
await withSerializationMode(serializationMode, () async {
await _isolateSpawnBenchmarks();
await _isolateSpawnUriBenchmarks();
await _separateProcessBenchmarks();
});
}
}
Future<void> _isolateSpawnBenchmarks() async {
void Function(SendPort) childIsolateFn(SerializationMode mode) =>
(SendPort sendPort) => withSerializationMode(mode, () {
var isolateReceivePort = ReceivePort();
isolateReceivePort.listen((data) {
deserialize(data);
var result = serialize();
result = result is Uint8List
? TransferableTypedData.fromList([result])
: result;
sendPort.send(result);
});
sendPort.send(isolateReceivePort.sendPort);
});
Completer? responseCompleter;
late SendPort sendPort;
var receivePort = ReceivePort();
var isolate = await Isolate.spawn(
childIsolateFn(serializationMode), receivePort.sendPort);
final sendPortCompleter = Completer<SendPort>();
receivePort.listen((data) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(data);
} else {
responseCompleter!.complete(data);
}
});
sendPort = await sendPortCompleter.future;
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
print('Isolate.spawn + $serializationMode: ${watch.elapsed}');
receivePort.close();
isolate.kill();
}
Future<void> _isolateSpawnUriBenchmarks() async {
Completer? responseCompleter;
late SendPort sendPort;
var receivePort = ReceivePort();
var isolate = await Isolate.spawnUri(
Uri.dataFromString(childProgram(serializationMode)),
[],
receivePort.sendPort);
final sendPortCompleter = Completer<SendPort>();
receivePort.listen((data) {
if (!sendPortCompleter.isCompleted) {
sendPortCompleter.complete(data);
} else {
responseCompleter!.complete(data);
}
});
sendPort = await sendPortCompleter.future;
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
result =
result is Uint8List ? TransferableTypedData.fromList([result]) : result;
sendPort.send(result);
deserialize(await responseCompleter.future);
}
print('Isolate.spawnUri + $serializationMode: ${watch.elapsed}');
receivePort.close();
isolate.kill();
}
Future<void> _separateProcessBenchmarks() async {
Completer? responseCompleter;
var tmpDir = Directory.systemTemp.createTempSync('serialize_bench');
try {
var file = File(tmpDir.uri.resolve('main.dart').toFilePath());
file.writeAsStringSync(childProgram(serializationMode));
var process = await Process.start(Platform.resolvedExecutable, [
'--packages=' + (await Isolate.packageConfig)!.toFilePath(),
file.path,
]);
var listeners = <StreamSubscription>[
process.stderr.listen((event) {
print('stderr: ${utf8.decode(event)}');
}),
process.stdout.listen((data) {
responseCompleter!.complete(data);
}),
];
// warmup
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
process.stdin.add(result);
} else {
process.stdin.writeln(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
// measure
var watch = Stopwatch()..start();
for (var i = 0; i < 100; i++) {
responseCompleter = Completer();
var result = serialize();
if (result is List<int>) {
process.stdin.add(result);
} else {
process.stdin.writeln(jsonEncode(result));
}
deserialize(await responseCompleter.future);
}
print('Separate process + $serializationMode: ${watch.elapsed}');
listeners.forEach((l) => l.cancel());
process.kill();
} catch (e, s) {
print('Error running benchmark \n$e\n\n$s');
} finally {
tmpDir.deleteSync(recursive: true);
}
}
String childProgram(SerializationMode mode) => '''
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:_fe_analyzer_shared/src/macros/executor/serialization.dart';
void main(_, [SendPort? sendPort]) {
var mode = $mode;
withSerializationMode(mode, () {
if (sendPort != null) {
var isolateReceivePort = ReceivePort();
isolateReceivePort.listen((data) {
deserialize(data);
var result = serialize();
result = result is Uint8List
? TransferableTypedData.fromList([result])
: result;
sendPort.send(result);
});
sendPort.send(isolateReceivePort.sendPort);
} else {
// We allow one empty line to work around some weird data.
var allowEmpty = true;
stdin.listen((data) {
if (mode == SerializationMode.jsonClient || mode == SerializationMode.jsonServer) {
var json = utf8.decode(data).trimRight();
// On exit we tend to get extra empty lines sometimes?
if (json.isEmpty && allowEmpty) {
allowEmpty = false;
return;
}
deserialize(jsonDecode(json));
stdout.write(jsonEncode(serialize()));
} else {
deserialize(data);
stdout.add(serialize() as List<int>);
}
});
}
});
}
Object? serialize() {
var serializer = serializerFactory();
for (var i = 0; i < 100; i++) {
serializer.addInt(i * 100);
serializer.addString('foo' * i);
serializer.addBool(i % 2 == 0);
serializer.startList();
for (var j = 0; j < 10; j++) {
serializer.addDouble(i * 5);
}
serializer.endList();
serializer.addNull();
}
return serializer.result;
}
void deserialize(Object? result) {
result = result is TransferableTypedData
? result.materialize().asUint8List()
: result;
var deserializer = deserializerFactory(result);
while (deserializer.moveNext()) {
deserializer
..expectInt()
..moveNext()
..expectString()
..moveNext()
..expectBool()
..moveNext()
..expectList();
while (deserializer.moveNext()) {
deserializer.expectDouble();
}
deserializer
..moveNext()
..checkNull();
}
}''';
Object? serialize() {
var serializer = serializerFactory();
for (var i = -50; i < 50; i++) {
serializer.addInt(i % 2 * 100);
serializer.addString('foo' * i);
serializer.addBool(i < 0);
serializer.startList();
for (var j = 0.0; j < 10; j++) {
serializer.addDouble(i * j);
}
serializer.endList();
serializer.addNull();
}
return serializer.result;
}
void deserialize(Object? result) {
result = result is TransferableTypedData
? result.materialize().asUint8List()
: result;
if (serializationMode == SerializationMode.jsonClient ||
serializationMode == SerializationMode.jsonServer) {
if (result is List<int>) {
result = jsonDecode(utf8.decode(result));
}
}
var deserializer = deserializerFactory(result);
while (deserializer.moveNext()) {
deserializer
..expectInt()
..moveNext()
..expectString()
..moveNext()
..expectBool()
..moveNext()
..expectList();
while (deserializer.moveNext()) {
deserializer.expectDouble();
}
deserializer
..moveNext()
..checkNull();
}
}