| // 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:convert'; |
| import 'dart:io'; |
| import 'dart:isolate'; |
| import 'dart:typed_data'; |
| |
| import 'package:benchmark_harness/benchmark_harness.dart' show BenchmarkBase; |
| |
| class JsonDecodingBenchmark { |
| JsonDecodingBenchmark(this.name, |
| {required this.sample, |
| required this.numTasks, |
| required this.useSendAndExit}); |
| |
| Future<void> report() async { |
| final stopwatch = Stopwatch()..start(); |
| // Benchmark harness counts 10 iterations as one. |
| for (int i = 0; i < 10; i++) { |
| final decodedFutures = <Future>[]; |
| for (int i = 0; i < numTasks; i++) { |
| decodedFutures.add(decodeJson(useSendAndExit, sample)); |
| } |
| await Future.wait(decodedFutures); |
| } |
| |
| print('$name(RunTime): ${stopwatch.elapsedMicroseconds} us.'); |
| } |
| |
| final String name; |
| final Uint8List sample; |
| final int numTasks; |
| final bool useSendAndExit; |
| } |
| |
| Uint8List createSampleJson(final size) { |
| final list = List.generate(size, (i) => i); |
| final map = <dynamic, dynamic>{}; |
| for (int i = 0; i < size; i++) { |
| map['$i'] = list; |
| } |
| return utf8.encode(json.encode(map)) as Uint8List; |
| } |
| |
| class JsonDecodeRequest { |
| final bool useSendAndExit; |
| final SendPort sendPort; |
| final Uint8List encodedJson; |
| const JsonDecodeRequest(this.useSendAndExit, this.sendPort, this.encodedJson); |
| } |
| |
| Future<Map> decodeJson(bool useSendAndExit, Uint8List encodedJson) async { |
| final port = ReceivePort(); |
| final inbox = StreamIterator<dynamic>(port); |
| final completer = Completer<bool>(); |
| final workerExitedPort = RawReceivePort((v) { |
| completer.complete(true); |
| }); |
| final workerErroredPort = RawReceivePort((v) { |
| stderr.writeln('worker errored out $v'); |
| completer.completeError(true); |
| }); |
| await Isolate.spawn(jsonDecodingIsolate, |
| JsonDecodeRequest(useSendAndExit, port.sendPort, encodedJson), |
| onError: workerErroredPort.sendPort, onExit: workerExitedPort.sendPort); |
| await completer.future; |
| workerExitedPort.close(); |
| workerErroredPort.close(); |
| await inbox.moveNext(); |
| final decodedJson = inbox.current as Map; |
| port.close(); |
| return decodedJson; |
| } |
| |
| Future<void> jsonDecodingIsolate(JsonDecodeRequest request) async { |
| final result = json.decode(utf8.decode(request.encodedJson)); |
| if (request.useSendAndExit) { |
| Isolate.exit(request.sendPort, result); |
| } else { |
| request.sendPort.send(result); |
| } |
| } |
| |
| class SyncJsonDecodingBenchmark extends BenchmarkBase { |
| SyncJsonDecodingBenchmark(String name, |
| {required this.sample, required this.iterations}) |
| : super(name); |
| |
| @override |
| void run() { |
| int l = 0; |
| for (int i = 0; i < iterations; i++) { |
| final Map map = json.decode(utf8.decode(sample)); |
| l += map.length; |
| } |
| assert(l > 0); |
| } |
| |
| final Uint8List sample; |
| final int iterations; |
| } |
| |
| class BenchmarkConfig { |
| BenchmarkConfig(this.suffix, this.sample); |
| |
| final String suffix; |
| final Uint8List sample; |
| } |
| |
| Future<void> main() async { |
| final jsonString = |
| File('benchmarks/IsolateJson/dart/sample.json').readAsStringSync(); |
| final json250KB = utf8.encode(jsonString) as Uint8List; // 294356 bytes |
| final decoded = json.decode(utf8.decode(json250KB)); |
| final decoded1MB = <dynamic, dynamic>{ |
| '1': decoded['1'], |
| '2': decoded['1'], |
| '3': decoded['1'], |
| '4': decoded['1'], |
| }; |
| final json1MB = |
| utf8.encode(json.encode(decoded1MB)) as Uint8List; // 1177397 bytes |
| decoded['1'] = (decoded['1'] as List).sublist(0, 200); |
| final json100KB = |
| utf8.encode(json.encode(decoded)) as Uint8List; // 104685 bytes |
| decoded['1'] = (decoded['1'] as List).sublist(0, 100); |
| final json50KB = |
| utf8.encode(json.encode(decoded)) as Uint8List; // 51760 bytes |
| |
| final configs = <BenchmarkConfig>[ |
| BenchmarkConfig('50KB', json50KB), |
| BenchmarkConfig('100KB', json100KB), |
| BenchmarkConfig('250KB', json250KB), |
| BenchmarkConfig('1MB', json1MB), |
| ]; |
| |
| for (final config in configs) { |
| for (final iterations in <int>[1, 4]) { |
| await JsonDecodingBenchmark( |
| 'IsolateJson.Decode${config.suffix}x$iterations', |
| useSendAndExit: false, |
| sample: config.sample, |
| numTasks: iterations) |
| .report(); |
| await JsonDecodingBenchmark( |
| 'IsolateJson.SendAndExit_Decode${config.suffix}x$iterations', |
| useSendAndExit: true, |
| sample: config.sample, |
| numTasks: iterations) |
| .report(); |
| SyncJsonDecodingBenchmark( |
| 'IsolateJson.SyncDecode${config.suffix}x$iterations', |
| sample: config.sample, |
| iterations: iterations) |
| .report(); |
| } |
| } |
| } |