Skip to content

Commit b9f68c8

Browse files
committedAug 5, 2022
Enable metrics and tracing on the benchmarker
1 parent 8101f55 commit b9f68c8

13 files changed

+148
-64
lines changed
 

‎Sources/RegexBenchmark/Benchmark.swift

+24-10
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import Foundation
33

44
protocol RegexBenchmark {
55
var name: String { get }
6-
func compile()
6+
mutating func compile()
77
func run()
88
func debug()
99
}
1010

1111
struct Benchmark: RegexBenchmark {
1212
let name: String
13-
let regex: Regex<AnyRegexOutput>
13+
var regex: Regex<AnyRegexOutput>
1414
let type: MatchType
1515
let target: String
1616

@@ -20,8 +20,15 @@ struct Benchmark: RegexBenchmark {
2020
case allMatches
2121
}
2222

23-
func compile() {
24-
blackHole(regex._compileRegex())
23+
mutating func compile() {
24+
let _ = regex._forceAction(.recompile)
25+
}
26+
27+
mutating func enableTracing() {
28+
let _ = regex._forceAction(.setOptions(.enableTracing))
29+
}
30+
mutating func enableMetrics() {
31+
let _ = regex._forceAction(.setOptions(.enableMetrics))
2532
}
2633

2734
func run() {
@@ -48,7 +55,8 @@ struct NSBenchmark: RegexBenchmark {
4855
case first
4956
}
5057

51-
func compile() {}
58+
// Not measured for NSRegularExpression
59+
mutating func compile() {}
5260

5361
func run() {
5462
switch type {
@@ -61,11 +69,17 @@ struct NSBenchmark: RegexBenchmark {
6169
/// A benchmark running a regex on strings in input set
6270
struct InputListBenchmark: RegexBenchmark {
6371
let name: String
64-
let regex: Regex<AnyRegexOutput>
72+
var regex: Regex<AnyRegexOutput>
6573
let targets: [String]
6674

67-
func compile() {
68-
blackHole(regex._compileRegex())
75+
mutating func compile() {
76+
blackHole(regex._forceAction(.recompile))
77+
}
78+
mutating func enableTracing() {
79+
let _ = regex._forceAction(.setOptions(.enableTracing))
80+
}
81+
mutating func enableMetrics() {
82+
let _ = regex._forceAction(.setOptions(.enableMetrics))
6983
}
7084

7185
func run() {
@@ -90,7 +104,7 @@ struct InputListNSBenchmark: RegexBenchmark {
90104
NSRange(target.startIndex..<target.endIndex, in: target)
91105
}
92106

93-
func compile() {}
107+
mutating func compile() {}
94108

95109
func run() {
96110
for target in targets {
@@ -160,7 +174,7 @@ struct CrossBenchmark {
160174
regex: nsRegex,
161175
type: .allMatches,
162176
target: input))
163-
if includeFirst {
177+
if includeFirst || runner.includeFirstOverride {
164178
runner.register(
165179
Benchmark(
166180
name: baseName + "First",

‎Sources/RegexBenchmark/BenchmarkRegistration.swift

+15-20
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,22 @@
22
// Do not remove the start of registration or end of registration markers
33

44
extension BenchmarkRunner {
5-
static func makeRunner(
6-
_ samples: Int,
7-
_ quiet: Bool
8-
) -> BenchmarkRunner {
9-
var benchmark = BenchmarkRunner("RegexBench", samples, quiet)
5+
mutating func registerDefault() {
106
// -- start of registrations --
11-
benchmark.addReluctantQuant()
12-
benchmark.addCSS()
13-
benchmark.addNotFound()
14-
benchmark.addGraphemeBreak()
15-
benchmark.addHangulSyllable()
16-
// benchmark.addHTML() // Disabled due to \b being unusably slow
17-
benchmark.addEmail()
18-
benchmark.addCustomCharacterClasses()
19-
benchmark.addBuiltinCC()
20-
benchmark.addUnicode()
21-
benchmark.addLiteralSearch()
22-
benchmark.addDiceNotation()
23-
benchmark.addErrorMessages()
24-
benchmark.addIpAddress()
7+
self.addReluctantQuant()
8+
self.addCSS()
9+
self.addNotFound()
10+
self.addGraphemeBreak()
11+
self.addHangulSyllable()
12+
// self.addHTML() // Disabled due to \b being unusably slow
13+
self.addEmail()
14+
self.addCustomCharacterClasses()
15+
self.addBuiltinCC()
16+
self.addUnicode()
17+
self.addLiteralSearch()
18+
self.addDiceNotation()
19+
self.addErrorMessages()
20+
self.addIpAddress()
2521
// -- end of registrations --
26-
return benchmark
2722
}
2823
}

‎Sources/RegexBenchmark/BenchmarkRunner.swift

+30-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import Foundation
2+
@_spi(RegexBenchmark) import _StringProcessing
23

34
struct BenchmarkRunner {
45
let suiteName: String
@@ -7,21 +8,43 @@ struct BenchmarkRunner {
78
let samples: Int
89
var results: SuiteResult = SuiteResult()
910
let quiet: Bool
10-
11-
init(_ suiteName: String, _ n: Int, _ quiet: Bool) {
12-
self.suiteName = suiteName
13-
self.samples = n
14-
self.quiet = quiet
11+
let enableTracing: Bool
12+
let enableMetrics: Bool
13+
14+
// Forcibly include firstMatch benchmarks for all CrossBenchmarks
15+
let includeFirstOverride: Bool
16+
17+
mutating func register(_ benchmark: some RegexBenchmark) {
18+
suite.append(benchmark)
1519
}
1620

17-
mutating func register(_ new: some RegexBenchmark) {
18-
suite.append(new)
21+
mutating func register(_ benchmark: Benchmark) {
22+
var benchmark = benchmark
23+
if enableTracing {
24+
benchmark.enableTracing()
25+
}
26+
if enableMetrics {
27+
benchmark.enableMetrics()
28+
}
29+
suite.append(benchmark)
30+
}
31+
32+
mutating func register(_ benchmark: InputListBenchmark) {
33+
var benchmark = benchmark
34+
if enableTracing {
35+
benchmark.enableTracing()
36+
}
37+
if enableMetrics {
38+
benchmark.enableMetrics()
39+
}
40+
suite.append(benchmark)
1941
}
2042

2143
mutating func measure(
2244
benchmark: some RegexBenchmark,
2345
samples: Int
2446
) -> BenchmarkResult {
47+
var benchmark = benchmark
2548
var runtimes: [Time] = []
2649
var compileTimes: [Time] = []
2750
// Initial run to make sure the regex has been compiled

‎Sources/RegexBenchmark/CLI.swift

+18-1
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,26 @@ struct Runner: ParsableCommand {
3737

3838
@Flag(help: "Exclude running NSRegex benchmarks")
3939
var excludeNs = false
40+
41+
@Flag(help: "Enable tracing of the engine (warning: lots of output)")
42+
var enableTracing: Bool = false
43+
44+
@Flag(help: "Enable engine metrics (warning: lots of output)")
45+
var enableMetrics: Bool = false
46+
47+
@Flag(help: "Include firstMatch benchmarks in CrossBenchmark (off by default")
48+
var includeFirst: Bool = false
4049

4150
mutating func run() throws {
42-
var runner = BenchmarkRunner.makeRunner(samples, quiet)
51+
var runner = BenchmarkRunner(
52+
suiteName: "DefaultRegexSuite",
53+
samples: samples,
54+
quiet: quiet,
55+
enableTracing: enableTracing,
56+
enableMetrics: enableMetrics,
57+
includeFirstOverride: includeFirst)
58+
59+
runner.registerDefault()
4360

4461
if !self.specificBenchmarks.isEmpty {
4562
runner.suite = runner.suite.filter { b in

‎Sources/RegexBenchmark/Debug.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ extension Benchmark {
66
case .whole:
77
let result = target.wholeMatch(of: regex)
88
if let match = result {
9-
if match.0.count > 100 {
9+
if match.0.count > 1000 {
1010
print("- Match: len = \(match.0.count)")
1111
} else {
1212
print("- Match: \(match.0)")
@@ -22,7 +22,7 @@ extension Benchmark {
2222
}
2323

2424
print("- Total matches: \(results.count)")
25-
if results.count > 10 {
25+
if results.count > 100 {
2626
print("# Too many matches, not printing")
2727
let avgLen = results.map({result in String(target[result.range]).count})
2828
.reduce(0.0, {$0 + Double($1)}) / Double(results.count)
@@ -32,7 +32,7 @@ extension Benchmark {
3232
}
3333

3434
for match in results {
35-
if match.0.count > 100 {
35+
if match.0.count > 1000 {
3636
print("- Match: len = \(match.0.count)")
3737
} else {
3838
print("- Match: \(match.0)")
@@ -42,7 +42,7 @@ extension Benchmark {
4242
case .first:
4343
let result = target.firstMatch(of: regex)
4444
if let match = result {
45-
if match.0.count > 100 {
45+
if match.0.count > 1000 {
4646
print("- Match: len = \(match.0.count)")
4747
} else {
4848
print("- Match: \(match.0)")
@@ -66,13 +66,13 @@ extension NSBenchmark {
6666
}
6767

6868
print("- Total matches: \(results.count)")
69-
if results.count > 10 {
69+
if results.count > 100 {
7070
print("# Too many matches, not printing")
7171
return
7272
}
7373

7474
for m in results {
75-
if m.range.length > 100 {
75+
if m.range.length > 1000 {
7676
print("- Match: len = \(m.range.length)")
7777
} else {
7878
print("- Match: \(target[Range(m.range, in: target)!])")
@@ -81,7 +81,7 @@ extension NSBenchmark {
8181
case .first:
8282
let result = regex.firstMatch(in: target, range: range)
8383
if let match = result {
84-
if match.range.length > 100 {
84+
if match.range.length > 1000 {
8585
print("- Match: len = \(match.range.length)")
8686
} else {
8787
print("- Match: \(target[Range(match.range, in: target)!])")

‎Sources/_StringProcessing/Engine/Consume.swift

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ extension Engine {
2121
subjectBounds: bounds,
2222
searchBounds: bounds,
2323
matchMode: matchMode,
24-
isTracingEnabled: enableTracing)
24+
isTracingEnabled: enableTracing,
25+
shouldMeasureMetrics: enableMetrics)
2526
}
2627

2728
func makeFirstMatchProcessor(
@@ -35,7 +36,8 @@ extension Engine {
3536
subjectBounds: subjectBounds,
3637
searchBounds: searchBounds,
3738
matchMode: .partialFromFront,
38-
isTracingEnabled: enableTracing)
39+
isTracingEnabled: enableTracing,
40+
shouldMeasureMetrics: enableMetrics)
3941
}
4042
}
4143

‎Sources/_StringProcessing/Engine/Engine.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ struct Engine {
2424
set { program.enableTracing = newValue }
2525
}
2626
var enableMetrics: Bool {
27-
get { program.enableTracing }
27+
get { program.enableMetrics }
2828
set { program.enableMetrics = newValue }
2929
}
3030

‎Sources/_StringProcessing/Engine/Instruction.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ struct Instruction: RawRepresentable, Hashable {
2222
}
2323

2424
extension Instruction {
25-
enum OpCode: UInt64, CaseIterable {
25+
enum OpCode: UInt64 {
2626
case invalid = 0
2727

2828
// MARK: - General Purpose
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,37 @@
11
extension Processor {
22
struct ProcessorMetrics {
3-
var instructionCounts: [Int] = .init(repeating: 0, count: Instruction.OpCode.allCases.count)
4-
var caseInsensitiveInstrs: Bool = false
3+
var instructionCounts: [Instruction.OpCode: Int] = [:]
4+
var backtracks: Int = 0
5+
var resets: Int = 0
56
}
67

78
func printMetrics() {
8-
// print("Total cycle count: \(cycleCount)")
9-
// print("Instructions:")
10-
let sorted = metrics.instructionCounts.enumerated()
9+
print("===")
10+
print("Total cycle count: \(cycleCount)")
11+
print("Backtracks: \(metrics.backtracks)")
12+
print("Resets: \(metrics.resets)")
13+
print("Instructions:")
14+
let sorted = metrics.instructionCounts
1115
.filter({$0.1 != 0})
1216
.sorted(by: { (a,b) in a.1 > b.1 })
1317
for (opcode, count) in sorted {
14-
print("\(Instruction.OpCode.init(rawValue: UInt64(opcode))!),\(count)")
18+
print("> \(opcode): \(count)")
19+
}
20+
print("===")
21+
}
22+
23+
mutating func measure() {
24+
let (opcode, _) = fetch().destructure
25+
if metrics.instructionCounts.keys.contains(opcode) {
26+
metrics.instructionCounts[opcode]! += 1
27+
} else {
28+
metrics.instructionCounts.updateValue(1, forKey: opcode)
1529
}
1630
}
1731

1832
mutating func measureMetrics() {
1933
if shouldMeasureMetrics {
20-
let (opcode, _) = fetch().destructure
21-
metrics.instructionCounts[Int(opcode.rawValue)] += 1
34+
measure()
2235
}
2336
}
2437
}

0 commit comments

Comments
 (0)
Please sign in to comment.