Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Add support for running perftools to use hardware performance counters when benchmarking. #98

Merged
merged 14 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 2 additions & 1 deletion lib/benchmark_harness.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
// BSD-style license that can be found in the LICENSE file.

export 'src/async_benchmark_base.dart';
export 'src/benchmark_base.dart';
export 'src/benchmark_base.dart'
if (dart.library.io) 'src/benchmark_base_perf.dart';
export 'src/score_emitter.dart';
16 changes: 14 additions & 2 deletions lib/src/benchmark_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ class BenchmarkBase {
/// Not measured teardown code executed after the benchmark runs.
void teardown() {}

/// Not measured code run just before starting the timed runs
void beforeTimedRuns() {}

/// Not measured code run just after the timed runs finish.
/// Receives the total number of iterations run.
void afterTimedRuns(int totalIterations) {}

/// Measures the score for this benchmark by executing it enough times
/// to reach [minimumMillis].
static _Measurement _measureForImpl(void Function() f, int minimumMillis) {
Expand All @@ -47,20 +54,22 @@ class BenchmarkBase {
final allowedJitter =
minimumMillis < 1000 ? 0 : (minimumMicros * 0.1).floor();
var iter = 2;
var totalIterations = iter;
final watch = Stopwatch()..start();
while (true) {
watch.reset();
for (var i = 0; i < iter; i++) {
f();
}
final elapsed = watch.elapsedMicroseconds;
final measurement = _Measurement(elapsed, iter);
final measurement = _Measurement(elapsed, iter, totalIterations);
if (measurement.elapsedMicros >= (minimumMicros - allowedJitter)) {
return measurement;
}

iter = measurement.estimateIterationsNeededToReach(
minimumMicros: minimumMicros);
totalIterations += iter;
}
}

Expand All @@ -74,8 +83,10 @@ class BenchmarkBase {
setup();
// Warmup for at least 100ms. Discard result.
_measureForImpl(warmup, 100);
beforeTimedRuns();
// Run the benchmark for at least 2000ms.
var result = _measureForImpl(exercise, _minimumMeasureDurationMillis);
afterTimedRuns(result.totalIterations);
teardown();
return result.score;
}
Expand All @@ -88,8 +99,9 @@ class BenchmarkBase {
class _Measurement {
final int elapsedMicros;
final int iterations;
final int totalIterations;

_Measurement(this.elapsedMicros, this.iterations);
_Measurement(this.elapsedMicros, this.iterations, this.totalIterations);

double get score => elapsedMicros / iterations;

Expand Down
60 changes: 60 additions & 0 deletions lib/src/benchmark_base_perf.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2023, 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:io';

import 'benchmark_base.dart' as base;
import 'score_emitter.dart';

const perfControlFifoVariable = 'PERF_CONTROL_FIFO';
const perfControlAckVariable = 'PERF_CONTROL_ACK';

class BenchmarkBase extends base.BenchmarkBase {
BenchmarkBase(super.name, {super.emitter = const PrintEmitter()});
whesse marked this conversation as resolved.
Show resolved Hide resolved

String? perfControlFifo;
late RandomAccessFile openedFifo;
String? perfControlAck;
late RandomAccessFile openedAck;

@override
void beforeTimedRuns() {
perfControlFifo = Platform.environment[perfControlFifoVariable];
perfControlAck = Platform.environment[perfControlAckVariable];
if (perfControlFifo != null) {
openedFifo = File(perfControlFifo!).openSync(mode: FileMode.writeOnly);
if (perfControlAck != null) {
openedAck = File(perfControlAck!).openSync();
openedFifo.writeStringSync('enable\n');
waitForAck();
} else {
openedFifo.writeStringSync('enable\n');
}
}
}

@override
void afterTimedRuns(int totalIterations) {
if (perfControlFifo != null) {
openedFifo.writeStringSync('disable\n');
openedFifo.closeSync();
if (perfControlAck != null) {
waitForAck();
openedAck.closeSync();
}
emitter.emit('$name.totalIterations', totalIterations.toDouble());
}
}

void waitForAck() {
var ack = <int>[...openedAck.readSync(4)];
while (ack.length < 4) {
ack.addAll(openedAck.readSync(4 - ack.length));
whesse marked this conversation as resolved.
Show resolved Hide resolved
print('reading $ack');
}
if (String.fromCharCodes(ack) != 'ack\n') {
print('Ack was $ack');
}
}
}
2 changes: 1 addition & 1 deletion test/result_emitter_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class MockResultEmitter extends ScoreEmitter {

// Create a new benchmark which has an emitter.
class BenchmarkWithResultEmitter extends BenchmarkBase {
const BenchmarkWithResultEmitter(ScoreEmitter emitter)
BenchmarkWithResultEmitter(ScoreEmitter emitter)
: super('Template', emitter: emitter);

@override
Expand Down