@@ -4,12 +4,13 @@ import Foundation
4
4
public protocol RegexBenchmark {
5
5
var name : String { get }
6
6
func run( )
7
+ func debug( )
7
8
}
8
9
9
10
public struct Benchmark : RegexBenchmark {
10
11
public let name : String
11
- let regex : Regex < Substring >
12
- let ty : MatchType
12
+ let regex : Regex < AnyRegexOutput >
13
+ let type : MatchType
13
14
let target : String
14
15
15
16
public enum MatchType {
@@ -19,7 +20,7 @@ public struct Benchmark: RegexBenchmark {
19
20
}
20
21
21
22
public func run( ) {
22
- switch ty {
23
+ switch type {
23
24
case . whole: blackHole ( target. wholeMatch ( of: regex) )
24
25
case . allMatches: blackHole ( target. matches ( of: regex) )
25
26
case . first: blackHole ( target. firstMatch ( of: regex) )
@@ -30,86 +31,100 @@ public struct Benchmark: RegexBenchmark {
30
31
public struct NSBenchmark : RegexBenchmark {
31
32
public let name : String
32
33
let regex : NSRegularExpression
33
- let ty : NSMatchType
34
+ let type : NSMatchType
34
35
let target : String
35
36
36
37
var range : NSRange {
37
38
NSRange ( target. startIndex..< target. endIndex, in: target)
38
39
}
39
40
40
41
public enum NSMatchType {
41
- case all
42
+ case allMatches
42
43
case first
43
44
}
44
45
45
46
public func run( ) {
46
- switch ty {
47
- case . all : blackHole ( regex. matches ( in: target, range: range) )
47
+ switch type {
48
+ case . allMatches : blackHole ( regex. matches ( in: target, range: range) )
48
49
case . first: blackHole ( regex. firstMatch ( in: target, range: range) )
49
50
}
50
51
}
51
52
}
52
53
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
58
-
59
- public init ( _ suiteName: String ) {
60
- self . suiteName = suiteName
61
- self . suite = [ ]
62
- self . samples = 20
63
- }
64
-
65
- public init ( _ suiteName: String , _ n: Int ) {
66
- self . suiteName = suiteName
67
- self . suite = [ ]
68
- self . samples = n
69
- }
54
+ /// A benchmark meant to be ran across multiple engines
55
+ struct CrossBenchmark {
56
+ /// The base name of the benchmark
57
+ var baseName : String
70
58
71
- public mutating func register( _ new: some RegexBenchmark ) {
72
- suite. append ( new)
73
- }
74
-
75
- func measure( benchmark: some RegexBenchmark ) -> Time {
76
- var times : [ Time ] = [ ]
77
-
78
- // initial run to make sure the regex has been compiled
79
- benchmark. run ( )
80
-
81
- // fixme: use suspendingclock?
82
- for _ in 0 ..< samples {
83
- let start = Tick . now
84
- benchmark. run ( )
85
- let end = Tick . now
86
- let time = end. elapsedTime ( since: start)
87
- times. append ( time)
88
- }
89
- // todo: compute stdev and warn if it's too large
90
-
91
- // return median time
92
- times. sort ( )
93
- return times [ samples/ 2 ]
94
- }
95
-
96
- public func run( ) {
97
- print ( " Running " )
98
- for b in suite {
99
- print ( " - \( b. name) \( measure ( benchmark: b) ) " )
59
+ /// The string to compile in differnet engines
60
+ var regex : String
61
+
62
+ /// The text to search
63
+ var input : String
64
+
65
+ // TODO: var output, for validation
66
+
67
+ /// Whether this is whole string matching or a searching benchmark
68
+ ///
69
+ /// TODO: Probably better ot have a whole-line vs search anywhere, maybe
70
+ /// accomodate multi-line matching, etc.
71
+ var isWhole : Bool = false
72
+
73
+ func register( _ runner: inout BenchmarkRunner ) {
74
+ let swiftRegex = try ! Regex ( regex)
75
+
76
+ let nsPattern = isWhole ? " ^ " + regex + " $ " : regex
77
+ let nsRegex : NSRegularExpression
78
+ if isWhole {
79
+ nsRegex = try ! NSRegularExpression ( pattern: " ^ " + regex + " $ " )
80
+ } else {
81
+ nsRegex = try ! NSRegularExpression ( pattern: regex)
100
82
}
101
- }
102
-
103
- public func profile( ) {
104
- print ( " Starting " )
105
- for b in suite {
106
- print ( " - \( b. name) " )
107
- b. run ( )
108
- print ( " - done " )
83
+
84
+ if isWhole {
85
+ runner. register (
86
+ Benchmark (
87
+ name: baseName + " Whole " ,
88
+ regex: swiftRegex,
89
+ type: . whole,
90
+ target: input) )
91
+ runner. register (
92
+ NSBenchmark (
93
+ name: baseName + " Whole_NS " ,
94
+ regex: nsRegex,
95
+ type: . first,
96
+ target: input) )
97
+ } else {
98
+ runner. register (
99
+ Benchmark (
100
+ name: baseName + " First " ,
101
+ regex: swiftRegex,
102
+ type: . first,
103
+ target: input) )
104
+ runner. register (
105
+ Benchmark (
106
+ name: baseName + " All " ,
107
+ regex: swiftRegex,
108
+ type: . allMatches,
109
+ target: input) )
110
+ runner. register (
111
+ NSBenchmark (
112
+ name: baseName + " First_NS " ,
113
+ regex: nsRegex,
114
+ type: . first,
115
+ target: input) )
116
+ runner. register (
117
+ NSBenchmark (
118
+ name: baseName + " All_NS " ,
119
+ regex: nsRegex,
120
+ type: . allMatches,
121
+ target: input) )
109
122
}
110
123
}
111
124
}
112
125
126
+ // TODO: Capture-containing benchmarks
127
+
113
128
// nom nom nom, consume the argument
114
129
@inline ( never)
115
130
public func blackHole< T> ( _ x: T ) {
0 commit comments