blob: 115e705cc35ffccccb4cf4ea711084e9ea49ff32 [file] [log] [blame]
// 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.
// @dart=2.9
import 'dart:async';
import 'dart:developer';
import 'dart:io';
import 'dart:isolate';
import 'dart:math' as math;
import 'package:compiler/src/dart2js.dart' as dart2js_main;
import 'package:vm_service/vm_service.dart' as vm_service;
import 'package:vm_service/vm_service_io.dart' as vm_service_io;
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 dart2js 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(
() => dart2js_main.internalMain(<String>[
'benchmarks/IsolateSpawnMemory/dart/helloworld.dart',
'--libraries-spec=sdk/lib/libraries.json'
]),
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;
}
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.
//
// We have two cases
//
// a) --enable-isolate-groups: All isolates will be within the same isolate
// group.
//
// b) --no-enable-isolate-groups: All isolates will be within their own,
// separate isolate group.
//
// In both cases we want to sum up the heap sizes of all isolate groups.
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) {
final isolateOrSentinel = await vmService.getIsolate(isolateRef.id);
if (isolateOrSentinel is vm_service.Isolate) {
groupIds.add(groupRef.id);
}
}
}
if (groupIds.isEmpty) {
throw 'Could not find main isolate';
}
return groupIds.toList();
}