Skip to content

Commit efe8986

Browse files
authored
Add a 'compile-once-test' subcommand (#840)
Adds a new subcommand to the RegexTester executable that demonstrates the difference between using regex patterns in different locations: - statically created - local to a function, but outside a loop - local to a function, inside a loop The compiler currently does not hoist the regex out of the loop, so it pays the parse/compile cost during every iteration.
1 parent 6432037 commit efe8986

File tree

3 files changed

+148
-2
lines changed

3 files changed

+148
-2
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import ArgumentParser
13+
import _RegexParser
14+
import _StringProcessing
15+
import Foundation
16+
17+
@available(SwiftStdlib 5.8, *)
18+
let litPattern = #/a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1/#
19+
@available(SwiftStdlib 5.8, *)
20+
let strPattern = try! Regex("a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1")
21+
22+
@available(SwiftStdlib 5.8, *)
23+
struct CompileOnceTest: ParsableCommand {
24+
static let litPattern = #/a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1/#
25+
static let strPattern = try! Regex("a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1")
26+
27+
var N: Int { 10_000 }
28+
var testString = "zzzzzzzzza"
29+
30+
func globalLitPattern() -> TimeInterval {
31+
let start = Date.now
32+
for _ in 0..<N {
33+
guard testString.contains(RegexTester.litPattern) else { return 0 }
34+
}
35+
return Date.now.timeIntervalSince(start)
36+
}
37+
38+
func globalStrPattern() -> TimeInterval {
39+
let start = Date.now
40+
for _ in 0..<N {
41+
guard testString.contains(RegexTester.strPattern) else { return 0 }
42+
}
43+
return Date.now.timeIntervalSince(start)
44+
}
45+
46+
func staticLitPattern() -> TimeInterval {
47+
let start = Date.now
48+
for _ in 0..<N {
49+
guard testString.contains(Self.litPattern) else { return 0 }
50+
}
51+
return Date.now.timeIntervalSince(start)
52+
}
53+
54+
func staticStrPattern() -> TimeInterval {
55+
let start = Date.now
56+
for _ in 0..<N {
57+
guard testString.contains(Self.strPattern) else { return 0 }
58+
}
59+
return Date.now.timeIntervalSince(start)
60+
}
61+
62+
func outerLitPattern() -> TimeInterval {
63+
let start = Date.now
64+
let pattern = #/a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1/#
65+
for _ in 0..<N {
66+
guard testString.contains(pattern) else { return 0 }
67+
}
68+
return Date.now.timeIntervalSince(start)
69+
}
70+
71+
func outerStrPattern() -> TimeInterval {
72+
let start = Date.now
73+
let pattern = try! Regex("a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1")
74+
for _ in 0..<N {
75+
guard testString.contains(pattern) else { return 0 }
76+
}
77+
return Date.now.timeIntervalSince(start)
78+
}
79+
80+
func innerLitPattern() -> TimeInterval {
81+
let start = Date.now
82+
for _ in 0..<N {
83+
let pattern = #/a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1/#
84+
guard testString.contains(pattern) else { return 0 }
85+
}
86+
return Date.now.timeIntervalSince(start)
87+
}
88+
89+
func innerStrPattern() -> TimeInterval {
90+
let start = Date.now
91+
for _ in 0..<N {
92+
let pattern = try! Regex("a+bcd(?:g*)ab+?c?de(?:(?:(?i:f)))hij|zzz++a|3|2|1")
93+
guard testString.contains(pattern) else { return 0 }
94+
}
95+
return Date.now.timeIntervalSince(start)
96+
}
97+
98+
func run() throws {
99+
let globalLit = globalLitPattern()
100+
let globalStr = globalStrPattern()
101+
let staticLit = staticLitPattern()
102+
let staticStr = staticStrPattern()
103+
let outerLit = outerLitPattern()
104+
let outerStr = outerStrPattern()
105+
let innerLit = innerLitPattern()
106+
let innerStr = innerStrPattern()
107+
108+
print("""
109+
Global literal: \(globalLit)
110+
string: \(globalStr)
111+
Static literal: \(staticLit)
112+
string: \(staticStr)
113+
Outer literal: \(outerLit)
114+
string: \(outerStr)
115+
Inner literal: \(innerLit)
116+
string: \(innerStr)
117+
""")
118+
}
119+
}

Sources/RegexTester/Main.swift

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2021-2022 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
//
10+
//===----------------------------------------------------------------------===//
11+
12+
import ArgumentParser
13+
import _RegexParser
14+
import _StringProcessing
15+
16+
@main
17+
@available(SwiftStdlib 5.8, *)
18+
struct Main: ParsableCommand {
19+
static var configuration: CommandConfiguration {
20+
.init(
21+
subcommands: [Tester.self, CompileOnceTest.self],
22+
defaultSubcommand: Tester.self)
23+
}
24+
}

Sources/RegexTester/RegexTester.swift

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ import ArgumentParser
1313
import _RegexParser
1414
import _StringProcessing
1515

16-
@main
1716
@available(SwiftStdlib 5.8, *)
18-
struct RegexTester: ParsableCommand {
17+
struct Tester: ParsableCommand {
18+
static var configuration: CommandConfiguration {
19+
.init(subcommands: [CompileOnceTest.self])
20+
}
21+
1922
typealias MatchFunctionType = (String) throws -> Regex<AnyRegexOutput>.Match?
2023

2124
@Argument(help: "The regex pattern to test.")

0 commit comments

Comments
 (0)