| // 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<void> 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 = <StreamIterator>[]; | |
| final continuations = <SendPort>[]; | |
| // 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<void> 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(<String>[ | |
| '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<int> 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<void> 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<List<String>> getGroupIds(vm_service.VmService vmService) async { | |
| final groupIds = <String>{}; | |
| 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(); | |
| } |