blob: 73b5d6e78c52350de50c8ef3e282e1e6587cd70a [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:collection/collection.dart';
import 'server.dart';
export 'src/benchmark_result.dart';
/// Returns the average of the benchmark results in [results].
///
/// Each element in [results] is expected to have identical benchmark names and
/// metrics; otherwise, an [Exception] will be thrown.
BenchmarkResults computeAverage(List<BenchmarkResults> results) {
if (results.isEmpty) {
throw ArgumentError('Cannot take average of empty list.');
}
final BenchmarkResults totalSum = results.reduce(
(BenchmarkResults sum, BenchmarkResults next) => sum._sumWith(next),
);
final BenchmarkResults average = totalSum;
for (final String benchmark in totalSum.scores.keys) {
final List<BenchmarkScore> scoresForBenchmark = totalSum.scores[benchmark]!;
for (int i = 0; i < scoresForBenchmark.length; i++) {
final BenchmarkScore score = scoresForBenchmark[i];
final double averageValue = score.value / results.length;
average.scores[benchmark]![i] =
BenchmarkScore(metric: score.metric, value: averageValue);
}
}
return average;
}
/// Computes the delta for each matching metric in [test] and [baseline], and
/// returns a new [BenchmarkResults] object where each [BenchmarkScore] contains
/// a [delta] value.
BenchmarkResults computeDelta(
BenchmarkResults baseline,
BenchmarkResults test,
) {
final Map<String, List<BenchmarkScore>> delta =
<String, List<BenchmarkScore>>{};
for (final String benchmarkName in test.scores.keys) {
final List<BenchmarkScore> testScores = test.scores[benchmarkName]!;
final List<BenchmarkScore>? baselineScores = baseline.scores[benchmarkName];
delta[benchmarkName] = testScores.map<BenchmarkScore>(
(BenchmarkScore testScore) {
final BenchmarkScore? baselineScore = baselineScores?.firstWhereOrNull(
(BenchmarkScore s) => s.metric == testScore.metric);
return testScore._copyWith(
delta: baselineScore == null
? null
: (testScore.value - baselineScore.value).toDouble(),
);
},
).toList();
}
return BenchmarkResults(delta);
}
extension _AnalysisExtension on BenchmarkResults {
/// Sums this [BenchmarkResults] instance with [other] by adding the values
/// of each matching benchmark score.
///
/// Returns a [BenchmarkResults] object with the summed values.
///
/// When [throwExceptionOnMismatch] is true (default), the set of benchmark
/// names and metric names in [other] are expected to be identical to those in
/// [scores], or else an [Exception] will be thrown.
BenchmarkResults _sumWith(
BenchmarkResults other, {
bool throwExceptionOnMismatch = true,
}) {
final Map<String, List<BenchmarkScore>> sum =
<String, List<BenchmarkScore>>{};
for (final String benchmark in scores.keys) {
// Look up this benchmark in [other].
final List<BenchmarkScore>? matchingBenchmark = other.scores[benchmark];
if (matchingBenchmark == null) {
if (throwExceptionOnMismatch) {
throw Exception(
'Cannot sum benchmarks because [other] is missing an entry for '
'benchmark "$benchmark".',
);
}
continue;
}
final List<BenchmarkScore> scoresForBenchmark = scores[benchmark]!;
sum[benchmark] =
scoresForBenchmark.map<BenchmarkScore>((BenchmarkScore score) {
// Look up this score in the [matchingBenchmark] from [other].
final BenchmarkScore? matchingScore = matchingBenchmark
.firstWhereOrNull((BenchmarkScore s) => s.metric == score.metric);
if (matchingScore == null && throwExceptionOnMismatch) {
throw Exception(
'Cannot sum benchmarks because benchmark "$benchmark" is missing '
'a score for metric ${score.metric}.',
);
}
return score._copyWith(
value: matchingScore == null
? score.value
: score.value + matchingScore.value,
);
}).toList();
}
return BenchmarkResults(sum);
}
}
extension _CopyExtension on BenchmarkScore {
BenchmarkScore _copyWith({String? metric, num? value, num? delta}) =>
BenchmarkScore(
metric: metric ?? this.metric,
value: value ?? this.value,
delta: delta ?? this.delta,
);
}