[vm/concurrency] Add SendPort.{Send,Receive}.* benchmarks

Other existing benchmarks that are related (e.g. IsolateJson) measure
multiple things at the same time (e.g. utf8 decoding speed, json
decoding speed, isolate communication).

The benchmarks in this CL focus purely on sending object graphs via [SendPort]s
and measure sending and receiving time. As data we measure json
(map/list/string heavy) as well as binary trees (user object heavy).

Issue https://github.com/dart-lang/sdk/issues/36097

TEST=Adding benchmark.

Change-Id: I032b5c63ee386697610ea2873914109789e00137
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/203763
Commit-Queue: Martin Kustermann <kustermann@google.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
diff --git a/benchmarks/SendPort/dart/SendPort.dart b/benchmarks/SendPort/dart/SendPort.dart
new file mode 100644
index 0000000..a6e6044
--- /dev/null
+++ b/benchmarks/SendPort/dart/SendPort.dart
@@ -0,0 +1,260 @@
+// Copyright (c) 2021, 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:isolate';
+
+// (Same data as used in our other Json* benchmarks)
+final data = '{"summary":{"turnover":0.3736,"correlation2":0.'
+    '7147,"concentration":0.3652,"beta":0.8814,"totalValue":1.3'
+    '091078259E8,"correlation":0.7217},"watchlist":[],"shortCash'
+    '":-1611000,"holdings":[{"type":"LONG","commission":1040'
+    ',"cost":9001920,"quantity":26000,"lots":[{"marketCap":"'
+    'L","industry":"TECHNOLOGY","style":"G","buyDate":"20'
+    '08-10-08 13:44:20.000","quantity":8000},{"marketCap":"L",'
+    '"industry":"TECHNOLOGY","style":"G","buyDate":"2008-1'
+    '0-15 13:28:02.000","quantity":18000}],"stock":"GOOG"},{"'
+    'type":"LONG","commission":8000,"cost":4672000,"quantity'
+    '":200000,"lots":[{"marketCap":"L","industry":"TECHNOLO'
+    'GY","style":"G","buyDate":"2008-10-15 13:28:54.000","q'
+    'uantity":200000}],"stock":"MSFT"},{"type":"LONG","comm'
+    'ission":21877,"cost":1.001592313E7,"quantity":546919,"lots'
+    '":[{"marketCap":"L","industry":"FINANCIAL","style":"'
+    'G","buyDate":"2008-08-01 09:50:17.000","quantity":103092}'
+    ',{"marketCap":"L","industry":"FINANCIAL","style":"G"'
+    ',"buyDate":"2008-08-18 10:31:34.000","quantity":49950},{"'
+    'marketCap":"L","industry":"FINANCIAL","style":"G","b'
+    'uyDate":"2008-08-29 09:35:22.000","quantity":45045},{"mark'
+    'etCap":"L","industry":"FINANCIAL","style":"G","buyDa'
+    'te":"2008-09-15 09:40:32.000","quantity":48400},{"marketCa'
+    'p":"L","industry":"FINANCIAL","style":"G","buyDate"'
+    ':"2008-10-06 11:21:50.000","quantity":432},{"marketCap":"'
+    'L","industry":"FINANCIAL","style":"G","buyDate":"200'
+    '8-10-15 13:30:05.000","quantity":300000}],"stock":"UBS"},'
+    '{"type":"LONG","commission":4000,"cost":6604849.1,"quan'
+    'tity":122741,"lots":[{"marketCap":"L","industry":"SERV'
+    'ICES","style":"V","buyDate":"2008-04-26 04:44:34.000",'
+    '"quantity":22741},{"marketCap":"L","industry":"SERVICES'
+    '","style":"V","buyDate":"2008-10-15 13:31:02.000","qua'
+    'ntity":100000}],"stock":"V"},{"type":"LONG","commissio'
+    'n":2805,"cost":5005558.25,"quantity":70121,"lots":[{"mar'
+    'ketCap":"M","industry":"RETAIL","style":"G","buyDate'
+    '":"2008-10-10 10:48:36.000","quantity":121},{"marketCap":'
+    '"M","industry":"RETAIL","style":"G","buyDate":"2008'
+    '-10-15 13:33:44.000","quantity":70000}],"stock":"LDG"},{'
+    '"type":"LONG","commission":10000,"cost":5382500,"quanti'
+    'ty":250000,"lots":[{"marketCap":"L","industry":"RETAIL'
+    '","style":"V","buyDate":"2008-10-15 13:34:30.000","qua'
+    'ntity":250000}],"stock":"SWY"},{"type":"LONG","commiss'
+    'ion":1120,"cost":1240960,"quantity":28000,"lots":[{"mark'
+    'etCap":"u","industry":"ETF","style":"B","buyDate":'
+    '"2008-10-15 15:57:39.000","quantity":28000}],"stock":"OIL'
+    '"},{"type":"LONG","commission":400,"cost":236800,"quan'
+    'tity":10000,"lots":[{"marketCap":"M","industry":"UTILI'
+    'TIES_AND_ENERGY","style":"G","buyDate":"2008-10-15 15:58'
+    ':03.000","quantity":10000}],"stock":"COG"},{"type":"LO'
+    'NG","commission":3200,"cost":1369600,"quantity":80000,"l'
+    'ots":[{"marketCap":"S","industry":"UTILITIES_AND_ENERGY'
+    '","style":"G","buyDate":"2008-10-15 15:58:32.000","qua'
+    'ntity":80000}],"stock":"CRZO"},{"type":"LONG","commiss'
+    'ion":429,"cost":108164.8,"quantity":10720,"lots":[{"mark'
+    'etCap":"u","industry":"FINANCIAL","style":"V","buyDa'
+    'te":"2008-10-16 09:37:06.000","quantity":10720}],"stock":'
+    '"FGI"},{"type":"LONG","commission":1080,"cost":494910,'
+    '"quantity":27000,"lots":[{"marketCap":"L","industry":'
+    '"RETAIL","style":"V","buyDate":"2008-10-16 09:37:06.000'
+    '","quantity":27000}],"stock":"LOW"},{"type":"LONG","'
+    'commission":4080,"cost":4867440,"quantity":102000,"lots":'
+    '[{"marketCap":"L","industry":"HEALTHCARE","style":"V'
+    '","buyDate":"2008-10-16 09:37:06.000","quantity":102000}]'
+    ',"stock":"AMGN"},{"type":"SHORT","commission":4000,"'
+    'cost":-1159000,"quantity":-100000,"lots":[{"marketCap":'
+    '"L","industry":"TECHNOLOGY","style":"V","buyDate":'
+    '"2008-10-16 09:37:06.000","quantity":-100000}],"stock":"'
+    'AMAT"},{"type":"LONG","commission":2,"cost":5640002,"'
+    'quantity":50,"lots":[{"marketCap":"L","industry":"FIN'
+    'ANCIAL","style":"B","buyDate":"2008-10-16 09:37:06.000'
+    '","quantity":50}],"stock":"BRKA"},{"type":"SHORT","'
+    'commission":4000,"cost":-436000,"quantity":-100000,"lots'
+    '":[{"marketCap":"M","industry":"TRANSPORTATION","styl'
+    'e":"G","buyDate":"2008-10-16 09:37:06.000","quantity":-'
+    '100000}],"stock":"JBLU"},{"type":"LONG","commission":8'
+    '000,"cost":1.1534E7,"quantity":200000,"lots":[{"marketCap'
+    '":"S","industry":"FINANCIAL","style":"G","buyDate":'
+    '"2008-10-16 14:35:24.000","quantity":200000}],"stock":"US'
+    'O"},{"type":"LONG","commission":4000,"cost":1.0129E7,"'
+    'quantity":100000,"lots":[{"marketCap":"L","industry":"'
+    'TECHNOLOGY","style":"G","buyDate":"2008-10-15 13:28:26.0'
+    '00","quantity":50000},{"marketCap":"L","industry":"TEC'
+    'HNOLOGY","style":"G","buyDate":"2008-10-17 09:33:09.000'
+    '","quantity":50000}],"stock":"AAPL"},{"type":"LONG",'
+    '"commission":1868,"cost":9971367.2,"quantity":54280,"lots'
+    '":[{"marketCap":"L","industry":"SERVICES","style":"G'
+    '","buyDate":"2008-04-26 04:44:34.000","quantity":7580},{'
+    '"marketCap":"L","industry":"SERVICES","style":"G","'
+    'buyDate":"2008-05-29 09:50:28.000","quantity":7500},{"mark'
+    'etCap":"L","industry":"SERVICES","style":"G","buyDat'
+    'e":"2008-10-15 13:30:38.000","quantity":33000},{"marketCap'
+    '":"L","industry":"SERVICES","style":"G","buyDate":'
+    '"2008-10-17 09:33:09.000","quantity":6200}],"stock":"MA"'
+    '}],"longCash":4.600368106E7,"ownerId":8,"pendingOrders":[{'
+    '"total":487000,"type":"cover","subtotal":483000,"price'
+    '":4.83,"commission":4000,"date":"2008-10-17 23:56:06.000"'
+    ',"quantity":100000,"expires":"2008-10-20 16:00:00.000","s'
+    'tock":"JBLU","id":182375},{"total":6271600,"type":"buy'
+    '","subtotal":6270000,"price":156.75,"commission":1600,"d'
+    'ate":"2008-10-17 23:56:40.000","quantity":40000,"expires"'
+    ':"2008-10-20 16:00:00.000","stock":"MA","id":182376}],"'
+    'inceptionDate":"2008-04-26 04:44:29.000","withdrawals":0,"'
+    'id":219948,"deposits":0}';
+
+class SendPortBenchmark {
+  final BenchmarkConfig config;
+  late ReceivePort port;
+  late StreamIterator it;
+
+  double usPerSend = 0.0;
+  double usPerReceive = 0.0;
+
+  SendPortBenchmark(this.config);
+
+  // Runs warmup phase, runs benchmark and reports result.
+  Future report() async {
+    port = ReceivePort();
+    it = StreamIterator(port);
+
+    // Warmup for 100 ms.
+    await measureFor(const Duration(milliseconds: 200));
+
+    // Run benchmark for 2 seconds.
+    //
+    // Sets [usPerSend] and [usPerReceive] as side-effect.
+    await measureFor(const Duration(seconds: 2));
+
+    // Report result.
+    print('SendPort.Send.${config.name}(RunTimeRaw): $usPerSend us.');
+    print('SendPort.Receive.${config.name}(RunTimeRaw): $usPerReceive us.');
+
+    await it.cancel();
+    port.close();
+  }
+
+  Future measureFor(Duration duration) async {
+    final durationInMicroseconds = duration.inMicroseconds;
+
+    int sumSendUs = 0;
+    int sumReceiveUs = 0;
+
+    final sw = Stopwatch()..start();
+
+    int numberOfSendReceives = 0;
+    int lastUs = 0;
+    int currentUs = 0;
+    do {
+      // Send & measure time
+      port.sendPort.send(config.data);
+      currentUs = sw.elapsedMicroseconds;
+      sumSendUs += currentUs - lastUs;
+      lastUs = currentUs;
+
+      // Receive & measure time
+      await it.moveNext();
+      it.current;
+      currentUs = sw.elapsedMicroseconds;
+      sumReceiveUs += currentUs - lastUs;
+      lastUs = currentUs;
+
+      numberOfSendReceives++;
+    } while (lastUs < durationInMicroseconds);
+
+    usPerSend = sumSendUs / numberOfSendReceives;
+    usPerReceive = sumReceiveUs / numberOfSendReceives;
+  }
+}
+
+class TreeNode {
+  final TreeNode? left;
+  final TreeNode? right;
+  final int value;
+
+  TreeNode(this.left, this.right, this.value);
+}
+
+TreeNode generateBinaryTreeOfDepth(int depth) {
+  int i = 0;
+
+  TreeNode gen(int depth) {
+    if (depth == 0) return TreeNode(null, null, i++);
+    return TreeNode(gen(depth - 1), gen(depth - 1), i++);
+  }
+
+  return gen(depth);
+}
+
+class BenchmarkConfig {
+  final String name;
+  final dynamic data;
+
+  BenchmarkConfig(this.name, this.data);
+}
+
+Future<void> main(args) async {
+  final String json5KB = data;
+  final json5KBDecoded = json.decode(json5KB);
+  assert(json5KB.length == 5534);
+
+  final json400B = json.encode(json5KBDecoded['pendingOrders']);
+  final json400BDecoded = json.decode(json400B);
+  assert(json400B.length == 390);
+
+  final String json50KB = json.encode({
+    '1': [json5KBDecoded, json5KBDecoded, json5KBDecoded, json5KBDecoded],
+    '2': [json5KBDecoded, json5KBDecoded, json5KBDecoded, json5KBDecoded],
+    '3': json5KBDecoded,
+  });
+  final json50KBDecoded = json.decode(json50KB);
+  assert(json50KB.length == 49814);
+
+  final String json500KB = json.encode({
+    '1': [json50KBDecoded, json50KBDecoded, json50KBDecoded, json50KBDecoded],
+    '2': [json50KBDecoded, json50KBDecoded, json50KBDecoded, json50KBDecoded],
+    '3': [json50KBDecoded, json50KBDecoded],
+  });
+  final json500KBDecoded = json.decode(json500KB);
+  assert(json500KB.length == 498169);
+
+  final String json5MB = json.encode({
+    '1': [
+      json500KBDecoded,
+      json500KBDecoded,
+      json500KBDecoded,
+    ],
+    '2': [json500KBDecoded, json500KBDecoded, json500KBDecoded],
+    '3': [json500KBDecoded, json500KBDecoded, json500KBDecoded],
+    '4': json500KBDecoded,
+  });
+  final json5MBDecoded = json.decode(json5MB);
+  assert(json5MB.length == 4981723);
+
+  final configs = <BenchmarkConfig>[
+    BenchmarkConfig('Nop', 1),
+    BenchmarkConfig('Json.400B', json400BDecoded),
+    BenchmarkConfig('Json.5KB', json5KBDecoded),
+    BenchmarkConfig('Json.50KB', json50KBDecoded),
+    BenchmarkConfig('Json.500KB', json500KBDecoded),
+    BenchmarkConfig('Json.5MB', json5MBDecoded),
+    BenchmarkConfig('BinaryTree.2', generateBinaryTreeOfDepth(2)),
+    BenchmarkConfig('BinaryTree.4', generateBinaryTreeOfDepth(4)),
+    BenchmarkConfig('BinaryTree.6', generateBinaryTreeOfDepth(6)),
+    BenchmarkConfig('BinaryTree.8', generateBinaryTreeOfDepth(8)),
+    BenchmarkConfig('BinaryTree.10', generateBinaryTreeOfDepth(10)),
+    BenchmarkConfig('BinaryTree.12', generateBinaryTreeOfDepth(12)),
+    BenchmarkConfig('BinaryTree.14', generateBinaryTreeOfDepth(14)),
+  ];
+
+  for (final config in configs) {
+    await SendPortBenchmark(config).report();
+  }
+}
diff --git a/benchmarks/SendPort/dart2/SendPort.dart b/benchmarks/SendPort/dart2/SendPort.dart
new file mode 100644
index 0000000..01d4042
--- /dev/null
+++ b/benchmarks/SendPort/dart2/SendPort.dart
@@ -0,0 +1,262 @@
+// Copyright (c) 2021, 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:convert';
+import 'dart:isolate';
+
+// (Same data as used in our other Json* benchmarks)
+final data = '{"summary":{"turnover":0.3736,"correlation2":0.'
+    '7147,"concentration":0.3652,"beta":0.8814,"totalValue":1.3'
+    '091078259E8,"correlation":0.7217},"watchlist":[],"shortCash'
+    '":-1611000,"holdings":[{"type":"LONG","commission":1040'
+    ',"cost":9001920,"quantity":26000,"lots":[{"marketCap":"'
+    'L","industry":"TECHNOLOGY","style":"G","buyDate":"20'
+    '08-10-08 13:44:20.000","quantity":8000},{"marketCap":"L",'
+    '"industry":"TECHNOLOGY","style":"G","buyDate":"2008-1'
+    '0-15 13:28:02.000","quantity":18000}],"stock":"GOOG"},{"'
+    'type":"LONG","commission":8000,"cost":4672000,"quantity'
+    '":200000,"lots":[{"marketCap":"L","industry":"TECHNOLO'
+    'GY","style":"G","buyDate":"2008-10-15 13:28:54.000","q'
+    'uantity":200000}],"stock":"MSFT"},{"type":"LONG","comm'
+    'ission":21877,"cost":1.001592313E7,"quantity":546919,"lots'
+    '":[{"marketCap":"L","industry":"FINANCIAL","style":"'
+    'G","buyDate":"2008-08-01 09:50:17.000","quantity":103092}'
+    ',{"marketCap":"L","industry":"FINANCIAL","style":"G"'
+    ',"buyDate":"2008-08-18 10:31:34.000","quantity":49950},{"'
+    'marketCap":"L","industry":"FINANCIAL","style":"G","b'
+    'uyDate":"2008-08-29 09:35:22.000","quantity":45045},{"mark'
+    'etCap":"L","industry":"FINANCIAL","style":"G","buyDa'
+    'te":"2008-09-15 09:40:32.000","quantity":48400},{"marketCa'
+    'p":"L","industry":"FINANCIAL","style":"G","buyDate"'
+    ':"2008-10-06 11:21:50.000","quantity":432},{"marketCap":"'
+    'L","industry":"FINANCIAL","style":"G","buyDate":"200'
+    '8-10-15 13:30:05.000","quantity":300000}],"stock":"UBS"},'
+    '{"type":"LONG","commission":4000,"cost":6604849.1,"quan'
+    'tity":122741,"lots":[{"marketCap":"L","industry":"SERV'
+    'ICES","style":"V","buyDate":"2008-04-26 04:44:34.000",'
+    '"quantity":22741},{"marketCap":"L","industry":"SERVICES'
+    '","style":"V","buyDate":"2008-10-15 13:31:02.000","qua'
+    'ntity":100000}],"stock":"V"},{"type":"LONG","commissio'
+    'n":2805,"cost":5005558.25,"quantity":70121,"lots":[{"mar'
+    'ketCap":"M","industry":"RETAIL","style":"G","buyDate'
+    '":"2008-10-10 10:48:36.000","quantity":121},{"marketCap":'
+    '"M","industry":"RETAIL","style":"G","buyDate":"2008'
+    '-10-15 13:33:44.000","quantity":70000}],"stock":"LDG"},{'
+    '"type":"LONG","commission":10000,"cost":5382500,"quanti'
+    'ty":250000,"lots":[{"marketCap":"L","industry":"RETAIL'
+    '","style":"V","buyDate":"2008-10-15 13:34:30.000","qua'
+    'ntity":250000}],"stock":"SWY"},{"type":"LONG","commiss'
+    'ion":1120,"cost":1240960,"quantity":28000,"lots":[{"mark'
+    'etCap":"u","industry":"ETF","style":"B","buyDate":'
+    '"2008-10-15 15:57:39.000","quantity":28000}],"stock":"OIL'
+    '"},{"type":"LONG","commission":400,"cost":236800,"quan'
+    'tity":10000,"lots":[{"marketCap":"M","industry":"UTILI'
+    'TIES_AND_ENERGY","style":"G","buyDate":"2008-10-15 15:58'
+    ':03.000","quantity":10000}],"stock":"COG"},{"type":"LO'
+    'NG","commission":3200,"cost":1369600,"quantity":80000,"l'
+    'ots":[{"marketCap":"S","industry":"UTILITIES_AND_ENERGY'
+    '","style":"G","buyDate":"2008-10-15 15:58:32.000","qua'
+    'ntity":80000}],"stock":"CRZO"},{"type":"LONG","commiss'
+    'ion":429,"cost":108164.8,"quantity":10720,"lots":[{"mark'
+    'etCap":"u","industry":"FINANCIAL","style":"V","buyDa'
+    'te":"2008-10-16 09:37:06.000","quantity":10720}],"stock":'
+    '"FGI"},{"type":"LONG","commission":1080,"cost":494910,'
+    '"quantity":27000,"lots":[{"marketCap":"L","industry":'
+    '"RETAIL","style":"V","buyDate":"2008-10-16 09:37:06.000'
+    '","quantity":27000}],"stock":"LOW"},{"type":"LONG","'
+    'commission":4080,"cost":4867440,"quantity":102000,"lots":'
+    '[{"marketCap":"L","industry":"HEALTHCARE","style":"V'
+    '","buyDate":"2008-10-16 09:37:06.000","quantity":102000}]'
+    ',"stock":"AMGN"},{"type":"SHORT","commission":4000,"'
+    'cost":-1159000,"quantity":-100000,"lots":[{"marketCap":'
+    '"L","industry":"TECHNOLOGY","style":"V","buyDate":'
+    '"2008-10-16 09:37:06.000","quantity":-100000}],"stock":"'
+    'AMAT"},{"type":"LONG","commission":2,"cost":5640002,"'
+    'quantity":50,"lots":[{"marketCap":"L","industry":"FIN'
+    'ANCIAL","style":"B","buyDate":"2008-10-16 09:37:06.000'
+    '","quantity":50}],"stock":"BRKA"},{"type":"SHORT","'
+    'commission":4000,"cost":-436000,"quantity":-100000,"lots'
+    '":[{"marketCap":"M","industry":"TRANSPORTATION","styl'
+    'e":"G","buyDate":"2008-10-16 09:37:06.000","quantity":-'
+    '100000}],"stock":"JBLU"},{"type":"LONG","commission":8'
+    '000,"cost":1.1534E7,"quantity":200000,"lots":[{"marketCap'
+    '":"S","industry":"FINANCIAL","style":"G","buyDate":'
+    '"2008-10-16 14:35:24.000","quantity":200000}],"stock":"US'
+    'O"},{"type":"LONG","commission":4000,"cost":1.0129E7,"'
+    'quantity":100000,"lots":[{"marketCap":"L","industry":"'
+    'TECHNOLOGY","style":"G","buyDate":"2008-10-15 13:28:26.0'
+    '00","quantity":50000},{"marketCap":"L","industry":"TEC'
+    'HNOLOGY","style":"G","buyDate":"2008-10-17 09:33:09.000'
+    '","quantity":50000}],"stock":"AAPL"},{"type":"LONG",'
+    '"commission":1868,"cost":9971367.2,"quantity":54280,"lots'
+    '":[{"marketCap":"L","industry":"SERVICES","style":"G'
+    '","buyDate":"2008-04-26 04:44:34.000","quantity":7580},{'
+    '"marketCap":"L","industry":"SERVICES","style":"G","'
+    'buyDate":"2008-05-29 09:50:28.000","quantity":7500},{"mark'
+    'etCap":"L","industry":"SERVICES","style":"G","buyDat'
+    'e":"2008-10-15 13:30:38.000","quantity":33000},{"marketCap'
+    '":"L","industry":"SERVICES","style":"G","buyDate":'
+    '"2008-10-17 09:33:09.000","quantity":6200}],"stock":"MA"'
+    '}],"longCash":4.600368106E7,"ownerId":8,"pendingOrders":[{'
+    '"total":487000,"type":"cover","subtotal":483000,"price'
+    '":4.83,"commission":4000,"date":"2008-10-17 23:56:06.000"'
+    ',"quantity":100000,"expires":"2008-10-20 16:00:00.000","s'
+    'tock":"JBLU","id":182375},{"total":6271600,"type":"buy'
+    '","subtotal":6270000,"price":156.75,"commission":1600,"d'
+    'ate":"2008-10-17 23:56:40.000","quantity":40000,"expires"'
+    ':"2008-10-20 16:00:00.000","stock":"MA","id":182376}],"'
+    'inceptionDate":"2008-04-26 04:44:29.000","withdrawals":0,"'
+    'id":219948,"deposits":0}';
+
+class SendPortBenchmark {
+  final BenchmarkConfig config;
+  ReceivePort port;
+  StreamIterator it;
+
+  double usPerSend = 0.0;
+  double usPerReceive = 0.0;
+
+  SendPortBenchmark(this.config);
+
+  // Runs warmup phase, runs benchmark and reports result.
+  Future report() async {
+    port = ReceivePort();
+    it = StreamIterator(port);
+
+    // Warmup for 100 ms.
+    await measureFor(const Duration(milliseconds: 200));
+
+    // Run benchmark for 2 seconds.
+    //
+    // Sets [usPerSend] and [usPerReceive] as side-effect.
+    await measureFor(const Duration(seconds: 2));
+
+    // Report result.
+    print('SendPort.Send.${config.name}(RunTimeRaw): $usPerSend us.');
+    print('SendPort.Receive.${config.name}(RunTimeRaw): $usPerReceive us.');
+
+    await it.cancel();
+    port.close();
+  }
+
+  Future measureFor(Duration duration) async {
+    final durationInMicroseconds = duration.inMicroseconds;
+
+    int sumSendUs = 0;
+    int sumReceiveUs = 0;
+
+    final sw = Stopwatch()..start();
+
+    int numberOfSendReceives = 0;
+    int lastUs = 0;
+    int currentUs = 0;
+    do {
+      // Send & measure time
+      port.sendPort.send(config.data);
+      currentUs = sw.elapsedMicroseconds;
+      sumSendUs += currentUs - lastUs;
+      lastUs = currentUs;
+
+      // Receive & measure time
+      await it.moveNext();
+      it.current;
+      currentUs = sw.elapsedMicroseconds;
+      sumReceiveUs += currentUs - lastUs;
+      lastUs = currentUs;
+
+      numberOfSendReceives++;
+    } while (lastUs < durationInMicroseconds);
+
+    usPerSend = sumSendUs / numberOfSendReceives;
+    usPerReceive = sumReceiveUs / numberOfSendReceives;
+  }
+}
+
+class TreeNode {
+  final TreeNode left;
+  final TreeNode right;
+  final int value;
+
+  TreeNode(this.left, this.right, this.value);
+}
+
+TreeNode generateBinaryTreeOfDepth(int depth) {
+  int i = 0;
+
+  TreeNode gen(int depth) {
+    if (depth == 0) return TreeNode(null, null, i++);
+    return TreeNode(gen(depth - 1), gen(depth - 1), i++);
+  }
+
+  return gen(depth);
+}
+
+class BenchmarkConfig {
+  final String name;
+  final dynamic data;
+
+  BenchmarkConfig(this.name, this.data);
+}
+
+Future<void> main(args) async {
+  final String json5KB = data;
+  final json5KBDecoded = json.decode(json5KB);
+  assert(json5KB.length == 5534);
+
+  final json400B = json.encode(json5KBDecoded['pendingOrders']);
+  final json400BDecoded = json.decode(json400B);
+  assert(json400B.length == 390);
+
+  final String json50KB = json.encode({
+    '1': [json5KBDecoded, json5KBDecoded, json5KBDecoded, json5KBDecoded],
+    '2': [json5KBDecoded, json5KBDecoded, json5KBDecoded, json5KBDecoded],
+    '3': json5KBDecoded,
+  });
+  final json50KBDecoded = json.decode(json50KB);
+  assert(json50KB.length == 49814);
+
+  final String json500KB = json.encode({
+    '1': [json50KBDecoded, json50KBDecoded, json50KBDecoded, json50KBDecoded],
+    '2': [json50KBDecoded, json50KBDecoded, json50KBDecoded, json50KBDecoded],
+    '3': [json50KBDecoded, json50KBDecoded],
+  });
+  final json500KBDecoded = json.decode(json500KB);
+  assert(json500KB.length == 498169);
+
+  final String json5MB = json.encode({
+    '1': [
+      json500KBDecoded,
+      json500KBDecoded,
+      json500KBDecoded,
+    ],
+    '2': [json500KBDecoded, json500KBDecoded, json500KBDecoded],
+    '3': [json500KBDecoded, json500KBDecoded, json500KBDecoded],
+    '4': json500KBDecoded,
+  });
+  final json5MBDecoded = json.decode(json5MB);
+  assert(json5MB.length == 4981723);
+
+  final configs = <BenchmarkConfig>[
+    BenchmarkConfig('Nop', 1),
+    BenchmarkConfig('Json.400B', json400BDecoded),
+    BenchmarkConfig('Json.5KB', json5KBDecoded),
+    BenchmarkConfig('Json.50KB', json50KBDecoded),
+    BenchmarkConfig('Json.500KB', json500KBDecoded),
+    BenchmarkConfig('Json.5MB', json5MBDecoded),
+    BenchmarkConfig('BinaryTree.2', generateBinaryTreeOfDepth(2)),
+    BenchmarkConfig('BinaryTree.4', generateBinaryTreeOfDepth(4)),
+    BenchmarkConfig('BinaryTree.6', generateBinaryTreeOfDepth(6)),
+    BenchmarkConfig('BinaryTree.8', generateBinaryTreeOfDepth(8)),
+    BenchmarkConfig('BinaryTree.10', generateBinaryTreeOfDepth(10)),
+    BenchmarkConfig('BinaryTree.12', generateBinaryTreeOfDepth(12)),
+    BenchmarkConfig('BinaryTree.14', generateBinaryTreeOfDepth(14)),
+  ];
+
+  for (final config in configs) {
+    await SendPortBenchmark(config).report();
+  }
+}