Skip to content

Commit

Permalink
Switch to tab-separated output, update CHANGELOG and version number
Browse files Browse the repository at this point in the history
The json output format is only in the most recent versions of perf stat.
Use the separated value output format instead.

Address review comments, ensure perf process is terminated if
failures occur.
  • Loading branch information
whesse committed Apr 18, 2024
1 parent be1a2c4 commit 931e4e7
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 50 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
## 2.2.3-wip
## 2.2.3

- Require Dart 3.2.
- Add `PerfBenchmarkBase` class which runs the 'perf stat' command from
linux-tools on a benchmark and reports metrics from the hardware
performance counters and the iteration count, as well as the run time
measurement reported by `BenchmarkBase`.

## 2.2.2

Expand Down
100 changes: 52 additions & 48 deletions lib/src/perf_benchmark_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,14 @@
// 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:async';
import 'dart:convert';
import 'dart:io';

import 'benchmark_base.dart';
import 'score_emitter.dart';

class PerfBenchmarkBase extends BenchmarkBase {
PerfBenchmarkBase(super.name, {super.emitter = const PrintEmitter()});

late final Directory fifoDir;
late final String perfControlFifo;
late final RandomAccessFile openedFifo;
Expand All @@ -19,37 +18,34 @@ class PerfBenchmarkBase extends BenchmarkBase {
late final Process perfProcess;
late final List<String> perfProcessArgs;

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

Future<void> _createFifos() async {
perfControlFifo = '${fifoDir.path}/perf_control_fifo';
perfControlAck = '${fifoDir.path}/perf_control_ack';

final fifoResult = await Process.run('mkfifo', [perfControlFifo]);
if (fifoResult.exitCode != 0) {
throw ProcessException('mkfifo', [perfControlFifo],
'Cannot create fifo: ${fifoResult.stderr}', fifoResult.exitCode);
}
final ackResult = await Process.run('mkfifo', [perfControlAck]);
if (ackResult.exitCode != 0) {
throw ProcessException('mkfifo', [perfControlAck],
'Cannot create fifo: ${ackResult.stderr}', ackResult.exitCode);
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);
}
}
}

Future<void> _startPerfStat() async {
await _createFifos();
perfProcessArgs = [
'stat',
'--delay',
'-1',
'--control',
'fifo:$perfControlFifo,$perfControlAck',
'-j',
'-p',
'$pid',
'--delay=-1',
'--control=fifo:$perfControlFifo,$perfControlAck',
'-x\\t',
'--pid=$pid',
];
perfProcess = await Process.start('perf', perfProcessArgs);
openedFifo = File(perfControlFifo).openSync(mode: FileMode.writeOnly);
}

void _enablePerf() {
openedFifo = File(perfControlFifo).openSync(mode: FileMode.writeOnly);
openedAck = File(perfControlAck).openSync();
openedFifo.writeStringSync('enable\n');
_waitForAck();
Expand All @@ -61,20 +57,36 @@ class PerfBenchmarkBase extends BenchmarkBase {
_waitForAck();
openedAck.closeSync();
perfProcess.kill(ProcessSignal.sigint);
final lines =
utf8.decoder.bind(perfProcess.stderr).transform(const LineSplitter());
// Exit code from perf is -2 when terminated with SIGINT.
unawaited(perfProcess.stdout.drain());
final lines = await perfProcess.stderr
.transform(utf8.decoder)
.transform(const LineSplitter())
.toList();
final exitCode = await perfProcess.exitCode;
if (exitCode != 0 && exitCode != -2) {
// Exit code from perf is -SIGINT when terminated with SIGINT.
if (exitCode != 0 && exitCode != -ProcessSignal.sigint.signalNumber) {
throw ProcessException(
'perf', perfProcessArgs, (await lines.toList()).join('\n'), exitCode);
'perf', perfProcessArgs, lines.join('\n'), exitCode);
}
final events = [
await for (final line in lines)
if (line.contains('"counter-value"'))
jsonDecode(line) as Map<String, dynamic>
];
_reportPerfStats(events, totalIterations);

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

/// Measures the score for the benchmark and returns it.
Expand All @@ -87,9 +99,15 @@ class PerfBenchmarkBase extends BenchmarkBase {
// Warmup for at least 100ms. Discard result.
measureForImpl(warmup, 100);
await _startPerfStat();
// Run the benchmark for at least 2000ms.
result = measureForImpl(exercise, minimumMeasureDurationMillis);
await _stopPerfStat(result.totalIterations);
try {
_enablePerf();
// Run the benchmark for at least 2000ms.
result = measureForImpl(exercise, minimumMeasureDurationMillis);
await _stopPerfStat(result.totalIterations);
} catch (_) {
perfProcess.kill(ProcessSignal.sigkill);
rethrow;
}
} finally {
await fifoDir.delete(recursive: true);
}
Expand All @@ -111,18 +129,4 @@ class PerfBenchmarkBase extends BenchmarkBase {
ack.addAll(openedAck.readSync(ackLength - ack.length));
}
}

void _reportPerfStats(List<Map<String, dynamic>> events, int iterations) {
for (final {'event': String event, 'counter-value': String counterString}
in events) {
final metric =
{'cycles:u': 'CpuCycles', 'page-faults:u': 'MajorPageFaults'}[event];
if (metric != null) {
emitter.emit(name, double.parse(counterString) / iterations,
metric: metric);
}
}
emitter.emit('$name.totalIterations', iterations.toDouble(),
metric: 'Count');
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: benchmark_harness
version: 2.2.3-wip
version: 2.2.3
description: The official Dart project benchmark harness.
repository: https://github.com/dart-lang/benchmark_harness

Expand Down

0 comments on commit 931e4e7

Please sign in to comment.