From e566f9a14bd44e023850de94064180ece8e25b2b Mon Sep 17 00:00:00 2001 From: Martin Kustermann Date: Mon, 30 May 2022 08:14:52 +0000 Subject: [PATCH] [vm/concurrency] Add IsolateBaseOverhead benchmark Issue b/190877061 TEST=ci Change-Id: Ifa570492b440849252a9bd01c7782656bcce7fd2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/245984 Commit-Queue: Martin Kustermann Reviewed-by: Alexander Aprelev --- .../dart/IsolateBaseOverhead.dart | 56 +++++++++++++++++++ .../dart2/IsolateBaseOverhead.dart | 56 +++++++++++++++++++ sdk/lib/isolate/isolate.dart | 3 + 3 files changed, 115 insertions(+) create mode 100644 benchmarks/IsolateBaseOverhead/dart/IsolateBaseOverhead.dart create mode 100644 benchmarks/IsolateBaseOverhead/dart2/IsolateBaseOverhead.dart diff --git a/benchmarks/IsolateBaseOverhead/dart/IsolateBaseOverhead.dart b/benchmarks/IsolateBaseOverhead/dart/IsolateBaseOverhead.dart new file mode 100644 index 00000000000..c9b8d2475e8 --- /dev/null +++ b/benchmarks/IsolateBaseOverhead/dart/IsolateBaseOverhead.dart @@ -0,0 +1,56 @@ +// 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:io'; +import 'dart:isolate'; + +const int count = 10000; + +// The benchmark will spawn a long chain of isolates, keeping all of them +// alive until the last one which measures Rss at that point (i.e. when all +// isolates are alive), thereby getting a good estimate of memory-overhead per +// isolate. +void main() async { + final onDone = ReceivePort(); + final lastIsolatePort = ReceivePort(); + final startRss = ProcessInfo.currentRss; + final startUs = DateTime.now().microsecondsSinceEpoch; + await Isolate.spawn(worker, WorkerInfo(count, lastIsolatePort.sendPort), + onExit: onDone.sendPort); + final result = await lastIsolatePort.first as List; + final lastIsolateRss = result[0] as int; + final lastIsolateUs = result[1] as int; + await onDone.first; + final doneUs = DateTime.now().microsecondsSinceEpoch; + + final averageMemoryUsageInKB = (lastIsolateRss - startRss) / count / 1024; + final averageStartLatencyInUs = (lastIsolateUs - startUs) / count; + final averageFinishLatencyInUs = (doneUs - startUs) / count; + + print('IsolateBaseOverhead.Rss(MemoryUse): $averageMemoryUsageInKB'); + print( + 'IsolateBaseOverhead.StartLatency(Latency): $averageStartLatencyInUs us.'); + print( + 'IsolateBaseOverhead.FinishLatency(Latency): $averageFinishLatencyInUs us.'); +} + +class WorkerInfo { + final int id; + final SendPort result; + + WorkerInfo(this.id, this.result); +} + +Future worker(WorkerInfo workerInfo) async { + if (workerInfo.id == 1) { + workerInfo.result + .send([ProcessInfo.currentRss, DateTime.now().microsecondsSinceEpoch]); + return; + } + final onExit = ReceivePort(); + await Isolate.spawn(worker, WorkerInfo(workerInfo.id - 1, workerInfo.result), + onExit: onExit.sendPort); + await onExit.first; +} diff --git a/benchmarks/IsolateBaseOverhead/dart2/IsolateBaseOverhead.dart b/benchmarks/IsolateBaseOverhead/dart2/IsolateBaseOverhead.dart new file mode 100644 index 00000000000..c9b8d2475e8 --- /dev/null +++ b/benchmarks/IsolateBaseOverhead/dart2/IsolateBaseOverhead.dart @@ -0,0 +1,56 @@ +// 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:io'; +import 'dart:isolate'; + +const int count = 10000; + +// The benchmark will spawn a long chain of isolates, keeping all of them +// alive until the last one which measures Rss at that point (i.e. when all +// isolates are alive), thereby getting a good estimate of memory-overhead per +// isolate. +void main() async { + final onDone = ReceivePort(); + final lastIsolatePort = ReceivePort(); + final startRss = ProcessInfo.currentRss; + final startUs = DateTime.now().microsecondsSinceEpoch; + await Isolate.spawn(worker, WorkerInfo(count, lastIsolatePort.sendPort), + onExit: onDone.sendPort); + final result = await lastIsolatePort.first as List; + final lastIsolateRss = result[0] as int; + final lastIsolateUs = result[1] as int; + await onDone.first; + final doneUs = DateTime.now().microsecondsSinceEpoch; + + final averageMemoryUsageInKB = (lastIsolateRss - startRss) / count / 1024; + final averageStartLatencyInUs = (lastIsolateUs - startUs) / count; + final averageFinishLatencyInUs = (doneUs - startUs) / count; + + print('IsolateBaseOverhead.Rss(MemoryUse): $averageMemoryUsageInKB'); + print( + 'IsolateBaseOverhead.StartLatency(Latency): $averageStartLatencyInUs us.'); + print( + 'IsolateBaseOverhead.FinishLatency(Latency): $averageFinishLatencyInUs us.'); +} + +class WorkerInfo { + final int id; + final SendPort result; + + WorkerInfo(this.id, this.result); +} + +Future worker(WorkerInfo workerInfo) async { + if (workerInfo.id == 1) { + workerInfo.result + .send([ProcessInfo.currentRss, DateTime.now().microsecondsSinceEpoch]); + return; + } + final onExit = ReceivePort(); + await Isolate.spawn(worker, WorkerInfo(workerInfo.id - 1, workerInfo.result), + onExit: onExit.sendPort); + await onExit.first; +} diff --git a/sdk/lib/isolate/isolate.dart b/sdk/lib/isolate/isolate.dart index 60d68999717..f3bd2ebd7e5 100644 --- a/sdk/lib/isolate/isolate.dart +++ b/sdk/lib/isolate/isolate.dart @@ -224,6 +224,9 @@ class Isolate { /// /// Returns a future which will complete with an [Isolate] instance if the /// spawning succeeded. It will complete with an error otherwise. + /// + /// One can expect the base memory overhead of an isolate to be in the order + /// of 30 kb. external static Future spawn( void entryPoint(T message), T message, {bool paused = false,