blob: 69043b91b5bd0d9b06c89e42b27684d90651ec23 [file] [log] [blame]
// Copyright (c) 2017, 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.
// This files contains methods for benchmarking Kernel binary serialization
// and deserialization routines.
import 'package:kernel/ast.dart';
import 'package:kernel/binary/ast_from_binary.dart';
import 'package:kernel/binary/ast_to_binary.dart';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
final usage = '''
Usage: binary_bench.dart [--golem|--raw] <Benchmark> <SourceDill>
Benchmark can be one of: ${benchmarks.keys.join(', ')}
''';
typedef void Benchmark(Uint8List bytes);
final benchmarks = <String, Benchmark>{
'AstFromBinaryEager': (Uint8List bytes) {
return _benchmarkAstFromBinary(bytes, eager: true);
},
'AstFromBinaryLazy': (Uint8List bytes) {
return _benchmarkAstFromBinary(bytes, eager: false);
},
'AstToBinary': (Uint8List bytes) {
return _benchmarkAstToBinary(bytes);
},
};
Benchmark? benchmark;
late File sourceDill;
bool forGolem = false;
bool forRaw = false;
void main(List<String> args) async {
if (!_parseArgs(args)) {
print(usage);
exit(-1);
}
final bytes = sourceDill.readAsBytesSync();
benchmark!(bytes);
}
const warmupIterations = 100;
const benchmarkIterations = 50;
void _benchmarkAstFromBinary(Uint8List bytes, {bool eager: true}) {
final sw = new Stopwatch()..start();
_fromBinary(bytes, eager: eager);
final coldRunUs = sw.elapsedMicroseconds;
sw.reset();
for (var i = 0; i < warmupIterations; i++) {
_fromBinary(bytes, eager: eager);
}
final warmupUs = sw.elapsedMicroseconds / warmupIterations;
final runsUs =
new List<int>.filled(benchmarkIterations, /* dummy value = */ 0);
for (var i = 0; i < benchmarkIterations; i++) {
sw.reset();
_fromBinary(bytes, eager: eager);
runsUs[i] = sw.elapsedMicroseconds;
}
final nameSuffix = eager ? 'Eager' : 'Lazy';
new BenchmarkResult('AstFromBinary${nameSuffix}', coldRunUs, warmupUs, runsUs)
.report();
}
void _benchmarkAstToBinary(Uint8List bytes) {
final p = _fromBinary(bytes, eager: true);
final sw = new Stopwatch()..start();
_toBinary(p);
final coldRunUs = sw.elapsedMicroseconds;
sw.reset();
for (var i = 0; i < warmupIterations; i++) {
_toBinary(p);
}
final warmupUs = sw.elapsedMicroseconds / warmupIterations;
final runsUs =
new List<int>.filled(benchmarkIterations, /* dummy value = */ 0);
for (var i = 0; i < benchmarkIterations; i++) {
sw.reset();
_toBinary(p);
runsUs[i] = sw.elapsedMicroseconds;
}
new BenchmarkResult('AstToBinary', coldRunUs, warmupUs, runsUs).report();
}
class BenchmarkResult {
final String name;
final int coldRunUs;
final double warmupUs;
final List<int> runsUs;
BenchmarkResult(this.name, this.coldRunUs, this.warmupUs, this.runsUs);
static T add<T extends num>(T x, T y) => x + y as T;
void report() {
runsUs.sort();
int P(int p) => runsUs[((runsUs.length - 1) * (p / 100)).ceil()];
final sum = runsUs.reduce(add);
final avg = sum / runsUs.length;
final min = runsUs.first;
final max = runsUs.last;
final std =
sqrt(runsUs.map((v) => pow(v - avg, 2)).reduce(add) / runsUs.length);
if (forGolem) {
print('${name}(RunTimeRaw): ${avg} us.');
print('${name}P50(RunTimeRaw): ${P(50)} us.');
print('${name}P90(RunTimeRaw): ${P(90)} us.');
} else if (forRaw) {
runsUs.forEach(print);
} else {
print('${name}Cold: ${coldRunUs} us');
print('${name}Warmup: ${warmupUs} us');
print('${name}: ${avg} us.');
final prefix = '-' * name.length;
print('${prefix}> Range: ${min}...${max} us.');
print('${prefix}> Std Dev: ${std.toStringAsFixed(2)}');
print('${prefix}> 50th percentile: ${P(50)} us.');
print('${prefix}> 90th percentile: ${P(90)} us.');
}
}
}
bool _parseArgs(List<String> args) {
if (args.length != 2 && args.length != 3) {
return false;
}
if (args[0] == '--golem') {
if (args.length != 3) {
return false;
}
forGolem = true;
args = args.skip(1).toList(growable: false);
} else if (args[0] == '--raw') {
if (args.length != 3) {
return false;
}
forRaw = true;
args = args.skip(1).toList(growable: false);
}
benchmark = benchmarks[args[0]];
if (benchmark == null) {
return false;
}
sourceDill = new File(args[1]);
if (!sourceDill.existsSync()) {
return false;
}
return true;
}
Component _fromBinary(List<int> bytes, {eager: true}) {
var component = new Component();
new BinaryBuilder(bytes, filename: 'filename', disableLazyReading: eager)
.readSingleFileComponent(component);
return component;
}
class SimpleSink implements Sink<List<int>> {
final List<List<int>> chunks = <List<int>>[];
@override
void add(List<int> chunk) {
chunks.add(chunk);
}
@override
void close() {}
}
void _toBinary(Component p) {
new BinaryPrinter(new SimpleSink()).writeComponentFile(p);
}