4ff04f641b
Change-Id: Icdcc648f42393b28daf648f752e3b4d61f51aa10 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/212043 Commit-Queue: Johnni Winther <johnniwinther@google.com> Reviewed-by: Jens Johansen <jensj@google.com>
196 lines
5.0 KiB
Dart
196 lines
5.0 KiB
Dart
// 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);
|
|
}
|