blob: 4a20f367fc61c0ceb12ca4ec165eee424b7e91e9 [file] [log] [blame]
// Copyright (c) 2022, 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 'util.dart';
typedef TestFunction<T> = List<T> Function(List<T>);
class InputOutputData<T> {
final List<T> input;
final List<T> output;
const InputOutputData(this.input, this.output);
}
const Strategy simpleAddStrategy = Strategy('simple-add', '''
Add entries to a list one at a time.''');
const Strategy spreadStrategy = Strategy('spread', '''
Create the list via spreads.''');
const Strategy listGenerateGrowableStrategy =
Strategy('list-generate-growable', '''
Create list via List.generate with growable = true.''');
const Strategy listGenerateNotGrowableStrategy =
Strategy('list-generate-not-growable', '''
Create list via List.generate with growable = false.''');
const Strategy listFilledGrowableStrategy = Strategy('list-filled-growable', '''
Create list via List.generate with growable = true.''');
const Strategy listFilledNotGrowableStrategy =
Strategy('list-filled-not-growable', '''
Create list via List.generate with growable = false.''');
const Strategy listFilledAlternativeGrowableStrategy =
Strategy('list-filled-alt-growable', '''
Create list via List.generate with growable = true in an alternative way.''');
const Strategy listFilledAlternativeNotGrowableStrategy =
Strategy('list-filled-alt-not-growable', '''
Create list via List.generate with growable = false in an alternative way.''');
const Scenario emptyScenario = Scenario('empty', '''
The input and output is empty.''');
const Scenario oneEntryScenario = Scenario('one', '''
The input is one entry.''');
const Scenario severalEntriesScenario = Scenario('several', '''
The input has several entries.''');
Map<Scenario, InputOutputData<String>> scenarios = {
emptyScenario: const InputOutputData([], []),
oneEntryScenario: const InputOutputData(["a"], ["<", "a", ">"]),
severalEntriesScenario: const InputOutputData(
[
"a",
"b",
"c",
"d",
"e",
"f",
"g",
],
[
"<",
"a",
", ",
"b",
", ",
"c",
", ",
"d",
", ",
"e",
", ",
"f",
", ",
"g",
">",
],
),
};
class Test<T> {
final int size;
final Map<Strategy, TestFunction<T>> strategies;
const Test(this.size, this.strategies);
void _test(Registry registry, SeriesKey key, int runs, int iterations,
List<T> input, List<T> expectedResult, TestFunction<T> testFunction) {
for (int run = 0; run < runs; run++) {
Stopwatch sw = new Stopwatch();
for (int i = 0; i < iterations; i++) {
sw.start();
List<T> actualOutput = testFunction(input);
sw.stop();
checkEquals(actualOutput, expectedResult);
}
registry.registerData(key, size, sw.elapsedMicroseconds);
}
}
void checkEquals(List<T> a, List<T> b) {
if (a.length != b.length) throw "length for $a vs $b";
for (int i = 0; i < a.length; i++) {
if (a[i] != b[i]) throw "index $i for $a vs $b";
}
}
void performTest(
{required Registry registry,
required int runs,
required int iterations,
required Map<Scenario, InputOutputData<T>> scenarios}) {
for (MapEntry<Scenario, InputOutputData<T>> scenario in scenarios.entries) {
List<T> scenarioInput = scenario.value.input;
List<T> scenarioExpectedOutput = scenario.value.output;
for (MapEntry<Strategy, TestFunction<T>> entry in strategies.entries) {
_test(registry, new SeriesKey(entry.key, scenario.key), runs,
iterations, scenarioInput, scenarioExpectedOutput, entry.value);
}
}
}
}
List<Test<String>> tests = [
// "size" isn't used here...
Test<String>(-1, {
simpleAddStrategy: simplyAdd,
listGenerateGrowableStrategy: listGenerateGrowable,
listGenerateNotGrowableStrategy: listGenerateNotGrowable,
listFilledGrowableStrategy: listFilledGrowable,
listFilledNotGrowableStrategy: listFilledNotGrowable,
listFilledAlternativeGrowableStrategy: listFilledAlternativeGrowable,
listFilledAlternativeNotGrowableStrategy: listFilledAlternativeNotGrowable,
spreadStrategy: spread,
}),
];
List<String> simplyAdd(List<String> input) {
if (input.isEmpty) return [];
List<String> result = [];
result.add("<");
result.add(input[0]);
for (int i = 1; i < input.length; i++) {
result.add(", ");
result.add(input[i]);
}
result.add(">");
return result;
}
List<String> listGenerateGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
return List<String>.generate(size, (index) {
if (index == 0) return "<";
if (index == size - 1) return ">";
if (index.isEven) return ", ";
return input[index >> 1];
}, growable: true);
}
List<String> listGenerateNotGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
return List<String>.generate(size, (index) {
if (index == 0) return "<";
if (index == size - 1) return ">";
if (index.isEven) return ", ";
return input[index >> 1];
}, growable: false);
}
List<String> listFilledAlternativeGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
List<String> result = List<String>.filled(size, ", ", growable: true);
for (int i = 0; i < input.length; i++) {
result[(i << 1) + 1] = input[i];
}
result[0] = "<";
result[result.length - 1] = ">";
return result;
}
List<String> listFilledAlternativeNotGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
List<String> result = List<String>.filled(size, ", ", growable: false);
for (int i = 0; i < input.length; i++) {
result[(i << 1) + 1] = input[i];
}
result[0] = "<";
result[result.length - 1] = ">";
return result;
}
List<String> listFilledGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
List<String> result = List<String>.filled(size, "", growable: true);
int j = 0;
result[j++] = "<";
result[j++] = input[0];
for (int i = 1; i < input.length; i++) {
result[j++] = ", ";
result[j++] = input[i];
}
result[j++] = ">";
return result;
}
List<String> listFilledNotGrowable(List<String> input) {
if (input.isEmpty) return [];
int size = input.length * 2 + 1;
List<String> result = List<String>.filled(size, "", growable: false);
int j = 0;
result[j++] = "<";
result[j++] = input[0];
for (int i = 1; i < input.length; i++) {
result[j++] = ", ";
result[j++] = input[i];
}
result[j++] = ">";
return result;
}
List<String> spread(List<String> input) {
return [
if (input.isNotEmpty) ...[
'<',
input.first,
for (String s in input.skip(1)) ...[', ', s],
'>',
]
];
}
void main() {
// Dry run
for (Test test in tests) {
test.performTest(
registry: new Registry(),
runs: 5,
iterations: 10,
scenarios: scenarios);
}
// Actual test
Registry registry = new Registry();
for (Test test in tests) {
test.performTest(
registry: registry, runs: 10, iterations: 100000, scenarios: scenarios);
}
SeriesSet seriesSet = registry.generateSeriesSet();
print('== Raw data ==');
for (Scenario scenario in scenarios.keys) {
print(seriesSet.getFullSpreadByScenario(scenario));
}
print('== Reduced averages ==');
SeriesSet reducedSeriesSet = seriesSet.filter((list) => removeMax(list, 3));
for (Scenario scenario in scenarios.keys) {
print(reducedSeriesSet.getAveragedSpreadByScenario(scenario));
}
}