| // Copyright (c) 2020, 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:convert'; |
| import 'dart:io' as io; |
| |
| import 'package:args/args.dart'; |
| import 'package:meta/meta.dart'; |
| |
| List<Benchmark> benchmarks = [ |
| DartStartup(), |
| DartRunStartup(), |
| SdkSize(), |
| ]; |
| |
| void main(List<String> args) async { |
| ArgParser argParser = _createArgParser(); |
| |
| ArgResults argResults; |
| |
| try { |
| argResults = argParser.parse(args); |
| } on FormatException catch (e) { |
| print(e.message); |
| print(''); |
| printUsage(argParser, includeDescription: false); |
| io.exit(1); |
| } |
| |
| if (argResults['help'] || argResults.arguments.isEmpty) { |
| printUsage(argParser); |
| io.exit(0); |
| } |
| |
| if (!argResults.wasParsed('dart-sdk')) { |
| print('No value passed for \`dart-sdk\`.'); |
| print(''); |
| printUsage(argParser); |
| io.exit(1); |
| } |
| |
| if (!argResults.wasParsed('run')) { |
| print('No value passed for \`run\`.'); |
| print(''); |
| printUsage(argParser); |
| io.exit(1); |
| } |
| |
| Context context = Context(argResults['dart-sdk']); |
| |
| String benchmarkName = argResults['run']; |
| Benchmark benchmark = benchmarks.singleWhere((b) => b.id == benchmarkName); |
| |
| BenchmarkResult result = await benchmark.run(context); |
| print(result.toJson()); |
| io.exit(0); |
| } |
| |
| void printUsage(ArgParser argParser, {bool includeDescription = true}) { |
| print('usage: dart bin/bench.dart <options>'); |
| print(''); |
| if (includeDescription) { |
| print('Run benchmarks for the dartdev tool.'); |
| print(''); |
| } |
| print('Options:'); |
| print(argParser.usage); |
| } |
| |
| ArgParser _createArgParser() { |
| ArgParser argParser = ArgParser(usageLineLength: io.stdout.terminalColumns); |
| argParser.addFlag( |
| 'help', |
| abbr: 'h', |
| negatable: false, |
| help: 'Print this usage information.', |
| ); |
| argParser.addOption( |
| 'dart-sdk', |
| valueHelp: 'sdk path', |
| help: 'The path to the Dart SDK to use for benchmarking.', |
| ); |
| argParser.addOption( |
| 'run', |
| valueHelp: 'benchmark', |
| allowed: benchmarks.map((b) => b.id).toList(), |
| allowedHelp: { |
| for (var benchmark in benchmarks) benchmark.id: benchmark.description, |
| }, |
| help: 'The benchmark to run.', |
| ); |
| return argParser; |
| } |
| |
| abstract class Benchmark { |
| final String id; |
| final String description; |
| |
| Benchmark(this.id, this.description); |
| |
| Future<BenchmarkResult> run(Context context); |
| } |
| |
| class DartStartup extends Benchmark { |
| DartStartup() |
| : super( |
| 'script-startup', |
| 'Benchmark the startup time of a minimal Dart script (μs).', |
| ); |
| |
| @override |
| Future<BenchmarkResult> run(Context context) async { |
| // setup |
| io.Directory dir = io.Directory.systemTemp.createTempSync('dartdev'); |
| io.File file = io.File('${dir.path}/hello.dart'); |
| file.writeAsStringSync('void main() => print(\'hello\');'); |
| |
| // perform the benchmark |
| Stopwatch timer = Stopwatch()..start(); |
| io.Process.runSync( |
| '${context.sdkPath}/bin/dart', |
| [file.absolute.path], |
| ); |
| timer.stop(); |
| |
| // cleanup |
| dir.deleteSync(recursive: true); |
| |
| // report the result |
| int micros = timer.elapsedMicroseconds; |
| return BenchmarkResult( |
| id: id, |
| value: micros, |
| units: 'microseconds', |
| userDescription: '${(micros / 1000.0).toStringAsFixed(2)}ms', |
| ); |
| } |
| } |
| |
| class DartRunStartup extends Benchmark { |
| DartRunStartup() |
| : super( |
| 'run-script-startup', |
| 'Benchmark the startup time of a minimal Dart script, executed with ' |
| '\`dart run\` (μs).', |
| ); |
| |
| @override |
| Future<BenchmarkResult> run(Context context) async { |
| // setup |
| io.Directory dir = io.Directory.systemTemp.createTempSync('dartdev'); |
| io.File file = io.File('${dir.path}/hello.dart'); |
| file.writeAsStringSync('void main() => print(\'hello\');'); |
| |
| // perform the benchmark |
| Stopwatch timer = Stopwatch()..start(); |
| io.Process.runSync( |
| '${context.sdkPath}/bin/dart', |
| ['run', file.absolute.path], |
| ); |
| timer.stop(); |
| |
| // cleanup |
| dir.deleteSync(recursive: true); |
| |
| // report the result |
| int micros = timer.elapsedMicroseconds; |
| return BenchmarkResult( |
| id: id, |
| value: micros, |
| units: 'microseconds', |
| userDescription: '${(micros / 1000.0).toStringAsFixed(2)}ms', |
| ); |
| } |
| } |
| |
| class SdkSize extends Benchmark { |
| SdkSize() |
| : super( |
| 'sdk-size', |
| 'Benchmark the compressed size of the Dart SDK (bytes).', |
| ); |
| |
| @override |
| Future<BenchmarkResult> run(Context context) async { |
| // setup |
| io.Directory tempDir = io.Directory.systemTemp.createTempSync('dartdev'); |
| |
| // perform the benchmark |
| io.File sdkArchive = compress(io.Directory(context.sdkPath), tempDir); |
| int bytes = sdkArchive.lengthSync(); |
| |
| // cleanup |
| tempDir.deleteSync(recursive: true); |
| |
| // report the result |
| return BenchmarkResult( |
| id: id, |
| value: bytes, |
| units: 'bytes', |
| userDescription: '${(bytes / (1024.0 * 1024.0)).toStringAsFixed(1)}MB', |
| ); |
| } |
| |
| io.File compress(io.Directory sourceDir, io.Directory targetDir) { |
| String name = sourceDir.path.substring(sourceDir.path.lastIndexOf('/') + 1); |
| io.File outFile = io.File('${targetDir.absolute.path}/$name.zip'); |
| |
| if (io.Platform.isMacOS || io.Platform.isLinux) { |
| io.Process.runSync('zip', [ |
| '-r', |
| '-9', // optimized for compressed size |
| outFile.absolute.path, |
| sourceDir.absolute.path, |
| ]); |
| } else { |
| throw Exception('platform not supported: ${io.Platform.operatingSystem}'); |
| } |
| |
| return outFile; |
| } |
| } |
| |
| class Context { |
| final String sdkPath; |
| |
| Context(this.sdkPath); |
| } |
| |
| class BenchmarkResult { |
| final String id; |
| final int value; |
| final String units; |
| final String userDescription; |
| |
| BenchmarkResult({ |
| @required this.id, |
| @required this.value, |
| @required this.units, |
| @required this.userDescription, |
| }); |
| |
| String toJson() { |
| Map m = { |
| 'id': id, |
| 'value': value, |
| 'units': units, |
| 'userDescription': userDescription, |
| }; |
| return JsonEncoder.withIndent(' ').convert(m); |
| } |
| } |