// 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:developer'; import 'dart:io'; import 'dart:isolate'; import 'dart:math' as math; import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service_io.dart' as vm_service_io; import '../../../pkg/vm/bin/gen_kernel.dart' as gen_kernel; const String compilerIsolateName = 'isolate-compiler'; class Result { const Result( this.rssOnStart, this.rssOnEnd, this.heapOnStart, this.heapOnEnd); final int rssOnStart; final int rssOnEnd; final int heapOnStart; final int heapOnEnd; } class StartMessage { const StartMessage(this.wsUri, this.sendPort); final String wsUri; final SendPort sendPort; } class SpawnMemory { SpawnMemory(this.name, this.wsUri); Future report() async { int maxProcessRss = 0; final timer = Timer.periodic(const Duration(microseconds: 100), (_) { maxProcessRss = math.max(maxProcessRss, ProcessInfo.currentRss); }); const numberOfBenchmarks = 3; final beforeRss = ProcessInfo.currentRss; final beforeHeap = await currentHeapUsage(wsUri); final iterators = []; final continuations = []; // Start all isolates & make them wait. for (int i = 0; i < numberOfBenchmarks; i++) { final receivePort = ReceivePort(); final startMessage = StartMessage(wsUri, receivePort.sendPort); await Isolate.spawn(isolateCompiler, startMessage, debugName: compilerIsolateName); final iterator = StreamIterator(receivePort); if (!await iterator.moveNext()) throw 'failed'; continuations.add(iterator.current as SendPort); iterators.add(iterator); } final readyRss = ProcessInfo.currentRss; final readyHeap = await currentHeapUsage(wsUri); // Let all isolates do the gen_kernel compilation. for (int i = 0; i < numberOfBenchmarks; i++) { final iterator = iterators[i]; final continuation = continuations[i]; continuation.send(null); if (!await iterator.moveNext()) throw 'failed'; if (iterator.current != 'done') throw 'failed'; } final doneRss = ProcessInfo.currentRss; final doneHeap = await currentHeapUsage(wsUri); // Shut down helper isolates for (int i = 0; i < numberOfBenchmarks; i++) { final iterator = iterators[i]; final continuation = continuations[i]; continuation.send(null); if (!await iterator.moveNext()) throw 'failed'; if (iterator.current != 'shutdown') throw 'failed'; await iterator.cancel(); } timer.cancel(); final readyDiffRss = math.max(0, readyRss - beforeRss) ~/ numberOfBenchmarks; final readyDiffHeap = math.max(0, readyHeap - beforeHeap) ~/ numberOfBenchmarks; final doneDiffRss = math.max(0, doneRss - beforeRss) ~/ numberOfBenchmarks; final doneDiffHeap = math.max(0, doneHeap - beforeHeap) ~/ numberOfBenchmarks; print('${name}RssOnStart(MemoryUse): $readyDiffRss'); print('${name}RssOnEnd(MemoryUse): $doneDiffRss'); print('${name}HeapOnStart(MemoryUse): $readyDiffHeap'); print('${name}HeapOnEnd(MemoryUse): $doneDiffHeap'); print('${name}PeakProcessRss(MemoryUse): $maxProcessRss'); } final String name; final String wsUri; } Future isolateCompiler(StartMessage startMessage) async { final port = ReceivePort(); final iterator = StreamIterator(port); // Let main isolate know we're ready. startMessage.sendPort.send(port.sendPort); await iterator.moveNext(); await runZoned( () => gen_kernel.compile([ 'benchmarks/IsolateSpawn/dart/helloworld.dart', 'benchmarks/IsolateSpawn/dart/helloworld.dart.dill', ]), zoneSpecification: ZoneSpecification( print: (Zone self, ZoneDelegate parent, Zone zone, String line) {})); // Let main isolate know we're done. startMessage.sendPort.send('done'); await iterator.moveNext(); // Closes the port. startMessage.sendPort.send('shutdown'); await iterator.cancel(); } Future currentHeapUsage(String wsUri) async { final vmService = await vm_service_io.vmServiceConnectUri(wsUri); final groupIds = await getGroupIds(vmService); int sum = 0; for (final groupId in groupIds) { final usage = await vmService.getIsolateGroupMemoryUsage(groupId); sum += usage.heapUsage! + usage.externalUsage!; } unawaited(vmService.dispose()); return sum; } Future main() async { // Only if we successfully reach the end will we set 0 exit code. exitCode = 255; final info = await Service.controlWebServer(enable: true); final observatoryUri = info.serverUri!; final wsUri = 'ws://${observatoryUri.authority}${observatoryUri.path}ws'; await SpawnMemory('IsolateSpawnMemory.Dart2JSDelta', wsUri).report(); // Only if we successfully reach the end will we set 0 exit code. exitCode = 0; } // Returns the set of isolate groups for which we should count the heap usage. Future> getGroupIds(vm_service.VmService vmService) async { final groupIds = {}; final vm = await vmService.getVM(); for (final groupRef in vm.isolateGroups!) { final group = await vmService.getIsolateGroup(groupRef.id!); for (final isolateRef in group.isolates!) { try { await vmService.getIsolate(isolateRef.id!); groupIds.add(groupRef.id!); break; } on vm_service.SentinelException catch (_) { // Skip groups with only sentinels. } } } if (groupIds.isEmpty) { throw 'Could not find main isolate'; } return groupIds.toList(); }