Skip to content

Commit ec32140

Browse files
committed
[benchmark] Run benchmarks using substring filters
Added support for running benchmarks using substring filters. Positional arguments prefixed with a single + or - sign are interpreted as benchmark name filters. Excecutes all benchmarks whose names include any of the strings prefixed with a plus sign but none of the strings prefixed with a minus sign.
1 parent ad24ca4 commit ec32140

File tree

3 files changed

+42
-8
lines changed

3 files changed

+42
-8
lines changed

benchmark/utils/ArgParse.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ class ArgumentParser<U> {
112112
.split(separator: "\n")
113113
.joined(separator: "\n" + padded(""))
114114
}
115-
let positional = f("TEST", "name or number of the benchmark to measure")
115+
let positional = f("TEST", "name or number of the benchmark to measure;\n"
116+
+ "use +/- prefix to filter by substring match")
116117
let optional = arguments.filter { $0.name != nil }
117118
.map { f($0.name!, $0.help ?? "") }
118119
.joined(separator: "\n")
@@ -152,7 +153,6 @@ class ArgumentParser<U> {
152153
/// We assume that optional switch args are of the form:
153154
///
154155
/// --opt-name[=opt-value]
155-
/// -opt-name[=opt-value]
156156
///
157157
/// with `opt-name` and `opt-value` not containing any '=' signs. Any
158158
/// other option passed in is assumed to be a positional argument.
@@ -165,7 +165,7 @@ class ArgumentParser<U> {
165165
for arg in CommandLine.arguments[1..<CommandLine.arguments.count] {
166166
// If the argument doesn't match the optional argument pattern. Add
167167
// it to the positional argument list and continue...
168-
if !arg.starts(with: "-") {
168+
if !arg.starts(with: "--") {
169169
positionalArgs.append(arg)
170170
continue
171171
}

benchmark/utils/DriverUtils.swift

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ struct TestConfig {
194194
afterRunSleep = c.afterRunSleep
195195
action = c.action ?? .run
196196
tests = TestConfig.filterTests(registeredBenchmarks,
197-
specifiedTests: Set(c.tests ?? []),
197+
tests: c.tests ?? [],
198198
tags: c.tags ?? [],
199199
skipTags: c.skipTags ?? [.unstable, .skip])
200200

@@ -241,10 +241,16 @@ struct TestConfig {
241241
/// specified filtering conditions.
242242
static func filterTests(
243243
_ registeredBenchmarks: [BenchmarkInfo],
244-
specifiedTests: Set<String>,
244+
tests: [String],
245245
tags: Set<BenchmarkCategory>,
246246
skipTags: Set<BenchmarkCategory>
247247
) -> [(index: String, info: BenchmarkInfo)] {
248+
var t = tests
249+
let filtersIndex = t.partition { $0.hasPrefix("+") || $0.hasPrefix("-") }
250+
let excludesIndex = t[filtersIndex...].partition { $0.hasPrefix("-") }
251+
let specifiedTests = Set(t[..<filtersIndex])
252+
let includes = t[filtersIndex..<excludesIndex].map { $0.dropFirst() }
253+
let excludes = t[excludesIndex...].map { $0.dropFirst() }
248254
let allTests = registeredBenchmarks.sorted()
249255
let indices = Dictionary(uniqueKeysWithValues:
250256
zip(allTests.map { $0.name },
@@ -256,14 +262,30 @@ struct TestConfig {
256262
}
257263
func byNamesOrIndices(b: BenchmarkInfo) -> Bool {
258264
return specifiedTests.contains(b.name) ||
259-
specifiedTests.contains(indices[b.name]!)
260-
} // !! "`allTests` have been assigned an index"
265+
// !! "`allTests` have been assigned an index"
266+
specifiedTests.contains(indices[b.name]!) ||
267+
(includes.contains { b.name.contains($0) } &&
268+
excludes.allSatisfy { !b.name.contains($0) } )
269+
}
261270
return allTests
262-
.filter(specifiedTests.isEmpty ? byTags : byNamesOrIndices)
271+
.filter(tests.isEmpty ? byTags : byNamesOrIndices)
263272
.map { (index: indices[$0.name]!, info: $0) }
264273
}
265274
}
266275

276+
extension String {
277+
func contains(_ str: Substring) -> Bool {
278+
guard let c = str.first else { return false }
279+
var s = self[...]
280+
repeat {
281+
s = s[(s.firstIndex(of: c) ?? s.endIndex)...]
282+
if s.starts(with: str) { return true }
283+
s = s.dropFirst()
284+
} while s.startIndex != s.endIndex
285+
return false
286+
}
287+
}
288+
267289
struct Stats {
268290
var n: Int = 0
269291
var S: Double = 0.0

test/benchmark/Benchmark_O.test.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ ALPHASORT: FatCompactMap
100100
101101
````
102102

103+
Substring filters using + and - prefix
104+
105+
````
106+
RUN: %Benchmark_O --list -.A +Angry -Small AngryPhonebook.ASCII.Small \
107+
RUN: | %FileCheck %s --check-prefix FILTERS
108+
FILTERS: AngryPhonebook.ASCII.Small
109+
FILTERS-NOT: AngryPhonebook.Armenian
110+
FILTERS-NOT: AngryPhonebook.Cyrillic.Small
111+
FILTERS: AngryPhonebook.Cyrillic
112+
FILTERS: AngryPhonebook.Strasse
113+
````
114+
103115
## Running Benchmarks
104116
By default, each real benchmark execution takes about a second per sample.
105117
To minimise the test time, multiple checks are combined into one run.

0 commit comments

Comments
 (0)