@@ -4,12 +4,13 @@ import Foundation
44public protocol RegexBenchmark {
55 var name : String { get }
66 func run( )
7+ func debug( )
78}
89
910public struct Benchmark : RegexBenchmark {
1011 public let name : String
11- let regex : Regex < Substring >
12- let ty : MatchType
12+ let regex : Regex < AnyRegexOutput >
13+ let type : MatchType
1314 let target : String
1415
1516 public enum MatchType {
@@ -19,7 +20,7 @@ public struct Benchmark: RegexBenchmark {
1920 }
2021
2122 public func run( ) {
22- switch ty {
23+ switch type {
2324 case . whole: blackHole ( target. wholeMatch ( of: regex) )
2425 case . allMatches: blackHole ( target. matches ( of: regex) )
2526 case . first: blackHole ( target. firstMatch ( of: regex) )
@@ -30,86 +31,100 @@ public struct Benchmark: RegexBenchmark {
3031public struct NSBenchmark : RegexBenchmark {
3132 public let name : String
3233 let regex : NSRegularExpression
33- let ty : NSMatchType
34+ let type : NSMatchType
3435 let target : String
3536
3637 var range : NSRange {
3738 NSRange ( target. startIndex..< target. endIndex, in: target)
3839 }
3940
4041 public enum NSMatchType {
41- case all
42+ case allMatches
4243 case first
4344 }
4445
4546 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) )
4849 case . first: blackHole ( regex. firstMatch ( in: target, range: range) )
4950 }
5051 }
5152}
5253
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
7058
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)
10082 }
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) )
109122 }
110123 }
111124}
112125
126+ // TODO: Capture-containing benchmarks
127+
113128// nom nom nom, consume the argument
114129@inline ( never)
115130public func blackHole< T> ( _ x: T ) {
0 commit comments