Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/benchmark_harness.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ jobs:
matrix:
# Add macos-latest and/or windows-latest if relevant for this package.
os: [ubuntu-latest]
sdk: [3.2, dev]
# Have to put `'3.10'` instead of `3.10` because of
# https://github.com/dart-lang/setup-dart/issues/161
sdk: ['3.10', dev]
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
- uses: dart-lang/setup-dart@e51d8e571e22473a2ddebf0ef8a2123f0ab2c02c
Expand Down
3 changes: 2 additions & 1 deletion pkgs/benchmark_harness/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## 2.4.0-wip
## 2.4.0

- Added a `bench` command.
- Require `sdk: ^3.10.0`.

## 2.3.1

Expand Down
10 changes: 5 additions & 5 deletions pkgs/benchmark_harness/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,26 @@ In other words, don't compare apples with oranges.

## Getting Started

1\. Add the following to your project's **pubspec.yaml**
1. Add the following to your project's **pubspec.yaml**

```yaml
dependencies:
benchmark_harness: any
```

2\. Install pub packages
2. Install pub packages

```sh
dart pub install
```

3\. Add the following import:
3. Add the following import:

```dart
import 'package:benchmark_harness/benchmark_harness.dart';
```

4\. Create a benchmark class which inherits from `BenchmarkBase` or
4. Create a benchmark class which inherits from `BenchmarkBase` or
`AsyncBenchmarkBase`.

## Example
Expand Down Expand Up @@ -113,7 +113,7 @@ Template(RunTime): 0.1568472448997197 us.

This is the average amount of time it takes to run `run()` 10 times for
`BenchmarkBase` and once for `AsyncBenchmarkBase`.
> µs is an abbreviation for microseconds.
> `us` is an abbreviation for microseconds.

## `bench` command

Expand Down
2 changes: 2 additions & 0 deletions pkgs/benchmark_harness/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# https://dart.dev/guides/language/analysis-options

include: package:dart_flutter_team_lints/analysis_options.yaml

analyzer:
Expand Down
4 changes: 3 additions & 1 deletion pkgs/benchmark_harness/lib/src/async_benchmark_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ class AsyncBenchmarkBase {
/// Measures the score for this benchmark by executing it repeatedly until
/// time minimum has been reached.
static Future<double> measureFor(
Future<void> Function() f, int minimumMillis) async {
Future<void> Function() f,
int minimumMillis,
) async {
final minimumMicros = minimumMillis * 1000;
final watch = Stopwatch()..start();
var iter = 0;
Expand Down
61 changes: 37 additions & 24 deletions pkgs/benchmark_harness/lib/src/bench_command/bench_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import 'package:args/args.dart';
enum RuntimeFlavor {
aot(help: 'Compile and run as a native binary.'),
jit(
help: 'Run as-is without compilation, '
help:
'Run as-is without compilation, '
'using the just-in-time (JIT) runtime.',
),
js(help: 'Compile to JavaScript and run on node.'),
Expand Down Expand Up @@ -36,13 +37,17 @@ class BenchOptions {
final result = _parserForBenchOptions.parse(args);

if (result.rest.isNotEmpty) {
throw FormatException('All arguments must be provided via `--` options. '
'Not sure what to do with "${result.rest.join()}".');
throw FormatException(
'All arguments must be provided via `--` options. '
'Not sure what to do with "${result.rest.join()}".',
);
}

return BenchOptions(
flavor:
result.multiOption('flavor').map(RuntimeFlavor.values.byName).toSet(),
flavor: result
.multiOption('flavor')
.map(RuntimeFlavor.values.byName)
.toSet(),
target: result.option('target')!,
help: result.flag('help'),
verbose: result.flag('verbose'),
Expand All @@ -60,23 +65,31 @@ class BenchOptions {
static String get usage => _parserForBenchOptions.usage;

static final _parserForBenchOptions = ArgParser()
..addMultiOption('flavor',
abbr: 'f',
allowed: RuntimeFlavor.values.map((e) => e.name),
allowedHelp: {
for (final flavor in RuntimeFlavor.values) flavor.name: flavor.help
})
..addOption('target',
defaultsTo: 'benchmark/benchmark.dart',
help: 'The target script to compile and run.')
..addFlag('help',
defaultsTo: false,
negatable: false,
help: 'Print usage information and quit.',
abbr: 'h')
..addFlag('verbose',
defaultsTo: false,
negatable: false,
help: 'Print the full stack trace if an exception is thrown.',
abbr: 'v');
..addMultiOption(
'flavor',
abbr: 'f',
allowed: RuntimeFlavor.values.map((e) => e.name),
allowedHelp: {
for (final flavor in RuntimeFlavor.values) flavor.name: flavor.help,
},
)
..addOption(
'target',
defaultsTo: 'benchmark/benchmark.dart',
help: 'The target script to compile and run.',
)
..addFlag(
'help',
defaultsTo: false,
negatable: false,
help: 'Print usage information and quit.',
abbr: 'h',
)
..addFlag(
'verbose',
defaultsTo: false,
negatable: false,
help: 'Print the full stack trace if an exception is thrown.',
abbr: 'v',
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ enum _Stage { compile, run }
/// Base class for runtime-specific runners.
abstract class _Runner {
_Runner._({required this.target, required this.flavor})
: assert(FileSystemEntity.isFileSync(target), '$target is not a file');
: assert(FileSystemEntity.isFileSync(target), '$target is not a file');

factory _Runner({required RuntimeFlavor flavor, required String target}) {
return (switch (flavor) {
Expand All @@ -58,8 +58,9 @@ abstract class _Runner {
///
/// Takes care of creating and deleting the corresponding temp directory.
Future<void> run() async {
_tempDirectory = Directory.systemTemp
.createTempSync('bench_${DateTime.now().millisecondsSinceEpoch}_');
_tempDirectory = Directory.systemTemp.createTempSync(
'bench_${DateTime.now().millisecondsSinceEpoch}_',
);
try {
await _runImpl();
} finally {
Expand All @@ -75,14 +76,20 @@ abstract class _Runner {
/// Also prints out a nice message before execution denoting the [flavor] and
/// the [stage].
Future<void> _runProc(
_Stage stage, String executable, List<String> args) async {
_Stage stage,
String executable,
List<String> args,
) async {
print('''
\n${flavor.name.toUpperCase()} - ${stage.name.toUpperCase()}
$executable ${args.join(' ')}
''');

final proc = await Process.start(executable, args,
mode: ProcessStartMode.inheritStdio);
final proc = await Process.start(
executable,
args,
mode: ProcessStartMode.inheritStdio,
);

final exitCode = await proc.exitCode;

Expand Down Expand Up @@ -156,14 +163,16 @@ class _WasmRunner extends _Runner {
outFile,
]);

final jsFile =
File.fromUri(_tempDirectory.uri.resolve('$_outputFileRoot.js'));
final jsFile = File.fromUri(
_tempDirectory.uri.resolve('$_outputFileRoot.js'),
);
jsFile.writeAsStringSync(_wasmInvokeScript);

await _runProc(_Stage.run, 'node', [jsFile.path]);
}

static const _wasmInvokeScript = '''
static const _wasmInvokeScript =
'''
import { readFile } from 'node:fs/promises'; // For async file reading
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
Expand Down
8 changes: 5 additions & 3 deletions pkgs/benchmark_harness/lib/src/measurement.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ Measurement measureForImpl(void Function() f, int minimumMillis) {
final minimumMicros = minimumMillis * 1000;
// If running a long measurement permit some amount of measurement jitter
// to avoid discarding results that are almost good, but not quite there.
final allowedJitter =
minimumMillis < 1000 ? 0 : (minimumMicros * 0.1).floor();
final allowedJitter = minimumMillis < 1000
? 0
: (minimumMicros * 0.1).floor();
var iter = 2;
var totalIterations = iter;
final watch = Stopwatch()..start();
Expand All @@ -29,7 +30,8 @@ Measurement measureForImpl(void Function() f, int minimumMillis) {
}

iter = measurement.estimateIterationsNeededToReach(
minimumMicros: minimumMicros);
minimumMicros: minimumMicros,
);
totalIterations += iter;
}
}
Expand Down
52 changes: 32 additions & 20 deletions pkgs/benchmark_harness/lib/src/perf_benchmark_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ class PerfBenchmarkBase extends BenchmarkBase {
late final Process perfProcess;
late final List<String> perfProcessArgs;

PerfBenchmarkBase(super.name,
{ScoreEmitterV2 super.emitter = const PrintEmitterV2()});
PerfBenchmarkBase(
super.name, {
ScoreEmitterV2 super.emitter = const PrintEmitterV2(),
});

ScoreEmitterV2 get _emitterV2 => emitter as ScoreEmitterV2;

Expand All @@ -30,8 +32,12 @@ class PerfBenchmarkBase extends BenchmarkBase {
for (final path in [perfControlFifo, perfControlAck]) {
final fifoResult = await Process.run('mkfifo', [path]);
if (fifoResult.exitCode != 0) {
throw ProcessException('mkfifo', [path],
'Cannot create fifo: ${fifoResult.stderr}', fifoResult.exitCode);
throw ProcessException(
'mkfifo',
[path],
'Cannot create fifo: ${fifoResult.stderr}',
fifoResult.exitCode,
);
}
}
}
Expand Down Expand Up @@ -70,27 +76,33 @@ class PerfBenchmarkBase extends BenchmarkBase {
// Exit code from perf is -SIGINT when terminated with SIGINT.
if (exitCode != 0 && exitCode != -ProcessSignal.sigint.signalNumber) {
throw ProcessException(
'perf', perfProcessArgs, lines.join('\n'), exitCode);
'perf',
perfProcessArgs,
lines.join('\n'),
exitCode,
);
}

const metrics = {
'cycles': 'CpuCycles',
'page-faults': 'MajorPageFaults',
};
const metrics = {'cycles': 'CpuCycles', 'page-faults': 'MajorPageFaults'};
for (final line in lines) {
if (line.split('\t')
case [
String counter,
_,
String event && ('cycles' || 'page-faults'),
...
]) {
_emitterV2.emit(name, double.parse(counter) / totalIterations,
metric: metrics[event]!);
if (line.split('\t') case [
String counter,
_,
String event && ('cycles' || 'page-faults'),
...,
]) {
_emitterV2.emit(
name,
double.parse(counter) / totalIterations,
metric: metrics[event]!,
);
}
}
_emitterV2.emit('$name.totalIterations', totalIterations.toDouble(),
metric: 'Count');
_emitterV2.emit(
'$name.totalIterations',
totalIterations.toDouble(),
metric: 'Count',
);
}

/// Measures the score for the benchmark and returns it.
Expand Down
16 changes: 12 additions & 4 deletions pkgs/benchmark_harness/lib/src/score_emitter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ class PrintEmitter implements ScoreEmitter {
/// be deprecated and removed. That release will be a breaking change.
abstract class ScoreEmitterV2 implements ScoreEmitter {
@override
void emit(String testName, double value,
{String metric = 'RunTime', String unit});
void emit(
String testName,
double value, {
String metric = 'RunTime',
String unit,
});
}

/// New implementation of [PrintEmitter] implementing the [ScoreEmitterV2]
Expand All @@ -31,8 +35,12 @@ class PrintEmitterV2 implements ScoreEmitterV2 {
const PrintEmitterV2();

@override
void emit(String testName, double value,
{String metric = 'RunTime', String unit = ''}) {
void emit(
String testName,
double value, {
String metric = 'RunTime',
String unit = '',
}) {
print(['$testName($metric):', value, if (unit.isNotEmpty) unit].join(' '));
}
}
4 changes: 2 additions & 2 deletions pkgs/benchmark_harness/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: benchmark_harness
version: 2.4.0-wip
version: 2.4.0
description: The official Dart project benchmark harness.
repository: https://github.com/dart-lang/tools/tree/main/pkgs/benchmark_harness
issue_tracker: https://github.com/dart-lang/tools/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Abenchmark_harness
Expand All @@ -8,7 +8,7 @@ topics:
- benchmarking

environment:
sdk: ^3.2.0
sdk: ^3.10.0

dependencies:
args: ^2.5.0
Expand Down
Loading
Loading