blob: a72de70f5967d1887ad9e66d5841d7e2bdb4cb97 [file] [log] [blame]
#!/usr/bin/env dart
// Copyright (c) 2016, 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 'package:kernel/kernel.dart';
import 'package:kernel/class_hierarchy.dart';
import 'dart:math';
import 'dart:io';
main(List<String> args) {
if (args.length == 0) {
print('USAGE: class_hierarchy_bench FILE.bart');
exit(1);
}
Program program = loadProgramFromBinary(args[0]);
var watch = new Stopwatch()..start();
var classHierarchy = new ClassHierarchy(program);
int coldBuildTime = watch.elapsedMilliseconds;
watch.reset();
const int numBuildTrials = 100;
for (int i = 0; i < numBuildTrials; i++) {
new ClassHierarchy(program);
}
int hotBuildTime = watch.elapsedMilliseconds ~/ numBuildTrials;
Random rnd = new Random(12345);
const int numQueryTrials = 100000; // should match 100K in output string.
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
classHierarchy.isSubclassOf(firstClass, secondClass);
}
int subclassQueryTime = watch.elapsedMilliseconds;
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
classHierarchy.isSubtypeOf(firstClass, secondClass);
}
int subtypeQueryTime = watch.elapsedMilliseconds;
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
int second = first - rnd.nextInt(100);
if (second < 0) {
second = 0;
}
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
classHierarchy.isSubclassOf(firstClass, secondClass);
}
int subclassDenseQueryTime = watch.elapsedMilliseconds;
int asInstanceOfQueryTimeSparse = 0;
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
int second = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
watch.reset();
classHierarchy.getClassAsInstanceOf(firstClass, secondClass);
asInstanceOfQueryTimeSparse += watch.elapsedMicroseconds;
}
asInstanceOfQueryTimeSparse ~/= 1000; // Convert to milliseconds
int asInstanceOfQueryTimeDense = 0;
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
Class firstClass = classHierarchy.classes[first];
Class candidate = firstClass;
Class secondClass = firstClass;
int steps = 0;
while (candidate != null) {
++steps;
if (rnd.nextInt(steps) == 0) {
secondClass = candidate;
}
candidate = candidate.superType?.classNode;
}
watch.reset();
classHierarchy.getClassAsInstanceOf(firstClass, secondClass);
asInstanceOfQueryTimeDense += watch.elapsedMicroseconds;
}
asInstanceOfQueryTimeDense ~/= 1000; // Convert to milliseconds
watch.reset();
for (int i = 0; i < numQueryTrials; i++) {
int first = rnd.nextInt(classHierarchy.classes.length);
int second = first - rnd.nextInt(100);
if (second < 0) {
second = 0;
}
Class firstClass = classHierarchy.classes[first];
Class secondClass = classHierarchy.classes[second];
classHierarchy.isSubtypeOf(firstClass, secondClass);
}
int subtypeDenseQueryTime = watch.elapsedMilliseconds;
List<int> depth = new List(classHierarchy.classes.length);
for (int i = 0; i < depth.length; ++i) {
int parentDepth = 0;
var classNode = classHierarchy.classes[i];
for (var superType in classNode.supers) {
var superClass = superType.classNode;
int index = classHierarchy.indexOf(superClass);
if (!(index < i)) {
throw '${classNode.name}($i) extends ${superClass.name}($index)';
}
assert(index < i);
parentDepth = max(parentDepth, depth[index]);
}
depth[i] = parentDepth + 1;
}
List<int> depthHistogram = getHistogramOf(depth);
double averageDepth = average(depth);
double medianDepth = median(depth);
int totalDepth = sum(depth);
int numberOfClasses = classHierarchy.classes.length;
print('''
classes: $numberOfClasses
build.cold: $coldBuildTime ms
build.hot: $hotBuildTime ms
isSubclassOf.random: $subclassQueryTime ms (per 100K)
isSubclassOf.dense: $subclassDenseQueryTime ms
isSubtypeOf.random: $subtypeQueryTime ms
isSubtypeOf.dense: $subtypeDenseQueryTime ms
asInstanceOf.sparse: $asInstanceOfQueryTimeSparse ms
asInstanceOf.dense: $asInstanceOfQueryTimeDense ms
expense-histogram: ${classHierarchy.getExpenseHistogram().join(' ')}
compression-ratio: ${classHierarchy.getCompressionRatio()}
depth-histogram: ${depthHistogram.join(' ')}
depth-average: $averageDepth
depth-median: $medianDepth
depth-total: $totalDepth
hash-table-size: ${classHierarchy.getSuperTypeHashTableSize()}
''');
}
List<int> getHistogramOf(Iterable<int> values) {
List<int> result = <int>[];
for (int value in values) {
while (result.length <= value) {
result.add(0);
}
++result[value];
}
return result;
}
double average(Iterable<num> values) {
double sum = 0.0;
int length = 0;
for (num x in values) {
sum += x;
++length;
}
return length == 0 ? 0.0 : sum / length;
}
double median(Iterable<num> values) {
List<num> list = values.toList(growable: false)..sort();
if (list.isEmpty) return 0.0;
int mid = list.length ~/ 2;
return list.length % 2 == 0
? ((list[mid] + list[mid + 1]) / 2)
: list[mid].toDouble();
}
num sum(Iterable<num> values) {
num result = 0;
for (var x in values) {
result += x;
}
return result;
}