Skip to content

Commit 6695027

Browse files
authored
Run scalar-semantic benchmark variants (#659)
Run scalar semantic benchmarks
1 parent 57b343d commit 6695027

File tree

3 files changed

+213
-94
lines changed

3 files changed

+213
-94
lines changed

Sources/RegexBenchmark/Benchmark.swift

Lines changed: 35 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@ struct NSBenchmark: RegexBenchmark {
7171
enum NSMatchType {
7272
case allMatches
7373
case first
74+
75+
init(_ type: Benchmark.MatchType) {
76+
switch type {
77+
case .whole, .first: self = .first
78+
case .allMatches: self = .allMatches
79+
}
80+
}
7481
}
7582

7683
func run() {
@@ -126,7 +133,7 @@ struct CrossBenchmark {
126133
/// The base name of the benchmark
127134
var baseName: String
128135

129-
/// The string to compile in differnet engines
136+
/// The string to compile in different engines
130137
var regex: String
131138

132139
/// The text to search
@@ -143,57 +150,32 @@ struct CrossBenchmark {
143150
/// Whether or not to do firstMatch as well or just allMatches
144151
var includeFirst: Bool = false
145152

153+
/// Whether to also run scalar-semantic mode
154+
var alsoRunScalarSemantic: Bool = true
155+
146156
func register(_ runner: inout BenchmarkRunner) {
147-
let swiftRegex = try! Regex(regex)
148-
let nsRegex: NSRegularExpression
149157
if isWhole {
150-
nsRegex = try! NSRegularExpression(pattern: "^" + regex + "$")
158+
runner.registerCrossBenchmark(
159+
nameBase: baseName,
160+
input: input,
161+
pattern: regex,
162+
.whole,
163+
alsoRunScalarSemantic: alsoRunScalarSemantic)
151164
} else {
152-
nsRegex = try! NSRegularExpression(pattern: regex)
153-
}
165+
runner.registerCrossBenchmark(
166+
nameBase: baseName,
167+
input: input,
168+
pattern: regex,
169+
.allMatches,
170+
alsoRunScalarSemantic: alsoRunScalarSemantic)
154171

155-
if isWhole {
156-
runner.register(
157-
Benchmark(
158-
name: baseName + "Whole",
159-
regex: swiftRegex,
160-
pattern: regex,
161-
type: .whole,
162-
target: input))
163-
runner.register(
164-
NSBenchmark(
165-
name: baseName + "Whole" + CrossBenchmark.nsSuffix,
166-
regex: nsRegex,
167-
type: .first,
168-
target: input))
169-
} else {
170-
runner.register(
171-
Benchmark(
172-
name: baseName + "All",
173-
regex: swiftRegex,
174-
pattern: regex,
175-
type: .allMatches,
176-
target: input))
177-
runner.register(
178-
NSBenchmark(
179-
name: baseName + "All" + CrossBenchmark.nsSuffix,
180-
regex: nsRegex,
181-
type: .allMatches,
182-
target: input))
183172
if includeFirst || runner.includeFirstOverride {
184-
runner.register(
185-
Benchmark(
186-
name: baseName + "First",
187-
regex: swiftRegex,
188-
pattern: regex,
189-
type: .first,
190-
target: input))
191-
runner.register(
192-
NSBenchmark(
193-
name: baseName + "First" + CrossBenchmark.nsSuffix,
194-
regex: nsRegex,
195-
type: .first,
196-
target: input))
173+
runner.registerCrossBenchmark(
174+
nameBase: baseName,
175+
input: input,
176+
pattern: regex,
177+
.first,
178+
alsoRunScalarSemantic: alsoRunScalarSemantic)
197179
}
198180
}
199181
}
@@ -209,20 +191,16 @@ struct CrossInputListBenchmark {
209191

210192
/// The list of strings to search
211193
var inputs: [String]
194+
195+
/// Also run in scalar-semantic mode
196+
var alsoRunScalarSemantic: Bool = true
212197

213198
func register(_ runner: inout BenchmarkRunner) {
214-
let swiftRegex = try! Regex(regex)
215-
runner.register(InputListBenchmark(
199+
runner.registerCrossBenchmark(
216200
name: baseName,
217-
regex: swiftRegex,
201+
inputList: inputs,
218202
pattern: regex,
219-
targets: inputs
220-
))
221-
runner.register(InputListNSBenchmark(
222-
name: baseName + CrossBenchmark.nsSuffix,
223-
regex: regex,
224-
targets: inputs
225-
))
203+
alsoRunScalarSemantic: alsoRunScalarSemantic)
226204
}
227205
}
228206

Sources/RegexBenchmark/BenchmarkRunner.swift

Lines changed: 141 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import Foundation
44
/// The number of times to re-run the benchmark if results are too varying
55
private var rerunCount: Int { 3 }
66

7+
extension Benchmark.MatchType {
8+
fileprivate var nameSuffix: String {
9+
switch self {
10+
case .whole: return "_Whole"
11+
case .first: return "_First"
12+
case .allMatches: return "_All"
13+
}
14+
}
15+
}
16+
717
struct BenchmarkRunner {
818
let suiteName: String
919
var suite: [any RegexBenchmark] = []
@@ -16,12 +26,141 @@ struct BenchmarkRunner {
1626

1727
// Forcibly include firstMatch benchmarks for all CrossBenchmarks
1828
let includeFirstOverride: Bool
29+
30+
// Register a cross-benchmark
31+
mutating func registerCrossBenchmark(
32+
nameBase: String,
33+
input: String,
34+
pattern: String,
35+
_ type: Benchmark.MatchType,
36+
alsoRunScalarSemantic: Bool = true
37+
) {
38+
let swiftRegex = try! Regex(pattern)
39+
let nsRegex: NSRegularExpression
40+
if type == .whole {
41+
nsRegex = try! NSRegularExpression(pattern: "^" + pattern + "$")
42+
} else {
43+
nsRegex = try! NSRegularExpression(pattern: pattern)
44+
}
45+
let nameSuffix = type.nameSuffix
46+
47+
register(
48+
Benchmark(
49+
name: nameBase + nameSuffix,
50+
regex: swiftRegex,
51+
pattern: pattern,
52+
type: type,
53+
target: input))
54+
register(
55+
NSBenchmark(
56+
name: nameBase + nameSuffix + CrossBenchmark.nsSuffix,
57+
regex: nsRegex,
58+
type: .init(type),
59+
target: input))
60+
61+
if alsoRunScalarSemantic {
62+
register(
63+
Benchmark(
64+
name: nameBase + nameSuffix + "_Scalar",
65+
regex: swiftRegex.matchingSemantics(.unicodeScalar),
66+
pattern: pattern,
67+
type: type,
68+
target: input))
69+
register(
70+
NSBenchmark(
71+
name: nameBase + nameSuffix + "_Scalar" + CrossBenchmark.nsSuffix,
72+
regex: nsRegex,
73+
type: .init(type),
74+
target: input))
75+
}
76+
}
77+
78+
// Register a cross-benchmark list
79+
mutating func registerCrossBenchmark(
80+
name: String,
81+
inputList: [String],
82+
pattern: String,
83+
alsoRunScalarSemantic: Bool = true
84+
) {
85+
let swiftRegex = try! Regex(pattern)
86+
register(InputListBenchmark(
87+
name: name,
88+
regex: swiftRegex,
89+
pattern: pattern,
90+
targets: inputList
91+
))
92+
register(InputListNSBenchmark(
93+
name: name + CrossBenchmark.nsSuffix,
94+
regex: pattern,
95+
targets: inputList
96+
))
97+
98+
if alsoRunScalarSemantic {
99+
register(InputListBenchmark(
100+
name: name + "_Scalar",
101+
regex: swiftRegex.matchingSemantics(.unicodeScalar),
102+
pattern: pattern,
103+
targets: inputList
104+
))
105+
register(InputListNSBenchmark(
106+
name: name + "_Scalar" + CrossBenchmark.nsSuffix,
107+
regex: pattern,
108+
targets: inputList
109+
))
110+
}
111+
112+
}
113+
114+
// Register a swift-only benchmark
115+
mutating func register(
116+
nameBase: String,
117+
input: String,
118+
pattern: String,
119+
_ swiftRegex: Regex<AnyRegexOutput>,
120+
_ type: Benchmark.MatchType,
121+
alsoRunScalarSemantic: Bool = true
122+
) {
123+
let nameSuffix = type.nameSuffix
124+
125+
register(
126+
Benchmark(
127+
name: nameBase + nameSuffix,
128+
regex: swiftRegex,
129+
pattern: pattern,
130+
type: type,
131+
target: input))
132+
133+
if alsoRunScalarSemantic {
134+
register(
135+
Benchmark(
136+
name: nameBase + nameSuffix + "_Scalar",
137+
regex: swiftRegex,
138+
pattern: pattern,
139+
type: type,
140+
target: input))
141+
}
142+
}
19143

20-
mutating func register(_ benchmark: some RegexBenchmark) {
144+
private mutating func register(_ benchmark: NSBenchmark) {
21145
suite.append(benchmark)
22146
}
23147

24-
mutating func register(_ benchmark: some SwiftRegexBenchmark) {
148+
private mutating func register(_ benchmark: Benchmark) {
149+
var benchmark = benchmark
150+
if enableTracing {
151+
benchmark.enableTracing()
152+
}
153+
if enableMetrics {
154+
benchmark.enableMetrics()
155+
}
156+
suite.append(benchmark)
157+
}
158+
159+
private mutating func register(_ benchmark: InputListNSBenchmark) {
160+
suite.append(benchmark)
161+
}
162+
163+
private mutating func register(_ benchmark: InputListBenchmark) {
25164
var benchmark = benchmark
26165
if enableTracing {
27166
benchmark.enableTracing()

Sources/RegexBenchmark/Suite/CustomCharacterClasses.swift

Lines changed: 37 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -12,53 +12,55 @@ extension BenchmarkRunner {
1212

1313
let input = Inputs.graphemeBreakData
1414

15-
register(Benchmark(
16-
name: "BasicCCC",
17-
regex: try! Regex(basic),
15+
// TODO: Which of these can be cross-benchmarks?
16+
17+
register(
18+
nameBase: "BasicCCC",
19+
input: input,
1820
pattern: basic,
19-
type: .allMatches,
20-
target: input))
21+
try! Regex(basic),
22+
.allMatches)
2123

22-
register(Benchmark(
23-
name: "BasicRangeCCC",
24-
regex: try! Regex(basicRange),
24+
register(
25+
nameBase: "BasicRangeCCC",
26+
input: input,
2527
pattern: basicRange,
26-
type: .allMatches,
27-
target: input))
28+
try! Regex(basicRange),
29+
.allMatches)
2830

29-
register(Benchmark(
30-
name: "CaseInsensitiveCCC",
31-
regex: try! Regex(caseInsensitive),
31+
register(
32+
nameBase: "CaseInsensitiveCCC",
33+
input: input,
3234
pattern: caseInsensitive,
33-
type: .allMatches,
34-
target: input))
35+
try! Regex(caseInsensitive),
36+
.allMatches)
3537

36-
register(Benchmark(
37-
name: "InvertedCCC",
38-
regex: try! Regex(inverted),
38+
register(
39+
nameBase: "InvertedCCC",
40+
input: input,
3941
pattern: inverted,
40-
type: .allMatches,
41-
target: input))
42+
try! Regex(inverted),
43+
.allMatches)
4244

43-
register(Benchmark(
44-
name: "SubtractionCCC",
45-
regex: try! Regex(subtraction),
45+
register(
46+
nameBase: "SubtractionCCC",
47+
input: input,
4648
pattern: subtraction,
47-
type: .allMatches,
48-
target: input))
49+
try! Regex(subtraction),
50+
.allMatches)
4951

50-
register(Benchmark(
51-
name: "IntersectionCCC",
52-
regex: try! Regex(intersection),
52+
register(
53+
nameBase: "IntersectionCCC",
54+
input: input,
5355
pattern: intersection,
54-
type: .allMatches,
55-
target: input))
56+
try! Regex(intersection),
57+
.allMatches)
5658

57-
register(Benchmark(
58-
name: "symDiffCCC",
59-
regex: try! Regex(symmetricDifference),
59+
register(
60+
nameBase: "symDiffCCC",
61+
input: input,
6062
pattern: symmetricDifference,
61-
type: .allMatches,
62-
target: input))
63+
try! Regex(symmetricDifference),
64+
.allMatches)
6365
}
6466
}

0 commit comments

Comments
 (0)