Skip to content

Commit 98992b0

Browse files
committed
v1 benchmarker
1 parent 2265620 commit 98992b0

File tree

10 files changed

+2338
-1
lines changed

10 files changed

+2338
-1
lines changed

Package.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ let package = Package(
4141
targets: ["_RegexParser"]),
4242
.executable(
4343
name: "VariadicsGenerator",
44-
targets: ["VariadicsGenerator"])
44+
targets: ["VariadicsGenerator"]),
45+
.executable(
46+
name: "RegexBenchmark",
47+
targets: ["RegexBenchmark"])
4548
],
4649
dependencies: [
4750
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
@@ -112,6 +115,17 @@ let package = Package(
112115
"_RegexParser",
113116
"_StringProcessing"
114117
]),
118+
.executableTarget(
119+
name: "RegexBenchmark",
120+
dependencies: [
121+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
122+
"_RegexParser",
123+
"_StringProcessing",
124+
"RegexBuilder"
125+
],
126+
swiftSettings: [
127+
.unsafeFlags(["-Xfrontend", "-disable-availability-checking"]),
128+
]),
115129

116130
// MARK: Exercises
117131
.target(
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import _StringProcessing
2+
import Foundation
3+
4+
public protocol RegexBenchmark {
5+
var name: String { get }
6+
func run()
7+
}
8+
9+
public struct Benchmark: RegexBenchmark {
10+
public let name: String
11+
let regex: Regex<Substring> // need to figure out this type to allow for other regexes
12+
let ty: MatchType
13+
let target: String
14+
15+
public enum MatchType {
16+
case whole
17+
case first
18+
case enumerate
19+
}
20+
21+
public func run() {
22+
switch ty {
23+
case .whole: blackHole(target.wholeMatch(of: regex))
24+
case .enumerate: blackHole(target.matches(of: regex))
25+
case .first: blackHole(target.firstMatch(of: regex))
26+
}
27+
}
28+
}
29+
30+
public struct NSBenchmark: RegexBenchmark {
31+
public let name: String
32+
let regex: NSRegularExpression
33+
let ty: NSMatchType
34+
let target: String
35+
36+
var range: NSRange {
37+
NSRange(target.startIndex..<target.endIndex, in: target)
38+
}
39+
40+
public enum NSMatchType {
41+
case all
42+
case first
43+
}
44+
45+
public func run() {
46+
switch ty {
47+
case .all: blackHole(regex.matches(in: target, range: range))
48+
case .first: blackHole(regex.firstMatch(in: target, range: range))
49+
}
50+
}
51+
}
52+
53+
public struct BenchmarkRunner {
54+
// Register instances of Benchmark and run them
55+
let suiteName: String
56+
var suite: [any RegexBenchmark]
57+
let samples: Int = 20
58+
59+
public init(suiteName: String) {
60+
self.suiteName = suiteName
61+
self.suite = []
62+
}
63+
64+
public mutating func register(new: some RegexBenchmark) {
65+
suite.append(new)
66+
}
67+
68+
// requires the macos13 beta
69+
// public func measure() -> Duration {
70+
// let clock = SuspendingClock()
71+
// var times = (0..<samples).map { _ in clock.measure(run) }
72+
// assert(times.count == samples)
73+
//
74+
// times.sort()
75+
// return times[samples/2]
76+
// }
77+
78+
func measure(benchmark: some RegexBenchmark) -> Time {
79+
var times: [Time] = []
80+
81+
// initial run to make sure the regex has been compiled
82+
benchmark.run()
83+
84+
for _ in 0..<samples {
85+
let start = Tick.now
86+
benchmark.run()
87+
let end = Tick.now
88+
let time = end.elapsedTime(since: start)
89+
times.append(time)
90+
}
91+
times.sort()
92+
return times[samples/2]
93+
}
94+
95+
public func run() {
96+
print("Running")
97+
for b in suite {
98+
print("- \(b.name) \(measure(benchmark: b))")
99+
}
100+
}
101+
}
102+
103+
// nom nom nom, consume the argument
104+
@inline(never)
105+
public func blackHole<T>(_ x: T) {
106+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import _StringProcessing
2+
import RegexBuilder
3+
import Foundation
4+
5+
// Tests that involve heavy backtracking
6+
7+
extension BenchmarkRunner {
8+
mutating func addBacktracking() {
9+
register(new: basicBacktrack)
10+
register(new: basicBacktrackNS)
11+
}
12+
}
13+
14+
15+
private let r = " +A"
16+
private let s = String(repeating: " ", count: 100)
17+
18+
private let basicBacktrack = Benchmark(
19+
name: "BasicBacktrack",
20+
regex: try! Regex(r),
21+
ty: .enumerate,
22+
target: s
23+
)
24+
25+
private let basicBacktrackNS = NSBenchmark(
26+
name: "BasicBacktrackNS",
27+
regex: try! NSRegularExpression(pattern: r),
28+
ty: .all,
29+
target: s
30+
)

0 commit comments

Comments
 (0)