Skip to content

Commit 205e4d3

Browse files
committed
Make comparison generic + Adjust stdev requirement to be a %
1 parent 1de8128 commit 205e4d3

File tree

4 files changed

+28
-57
lines changed

4 files changed

+28
-57
lines changed

Sources/RegexBenchmark/Benchmark.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,6 @@ protocol RegexBenchmark {
1111
protocol SwiftRegexBenchmark: RegexBenchmark {
1212
var regex: Regex<AnyRegexOutput> { get set }
1313
var pattern: String? { get }
14-
mutating func compile()
15-
mutating func parse() -> Bool
16-
mutating func enableTracing()
17-
mutating func enableMetrics()
1814
}
1915

2016
extension SwiftRegexBenchmark {

Sources/RegexBenchmark/BenchmarkResults.swift

Lines changed: 24 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -161,12 +161,12 @@ struct BenchmarkResult: Codable, CustomStringConvertible {
161161
let parseTime: Measurement?
162162

163163
var description: String {
164-
var base = "> run time: \(runtime.description)"
164+
var base = " > run time: \(runtime.description)"
165165
if let compileTime = compileTime {
166-
base += "\n> compile time: \(compileTime)"
166+
base += "\n > compile time: \(compileTime)"
167167
}
168168
if let parseTime = parseTime {
169-
base += "\n> parse time: \(parseTime)"
169+
base += "\n > parse time: \(parseTime)"
170170
}
171171
return base
172172
}
@@ -176,45 +176,17 @@ extension BenchmarkResult {
176176
struct Comparison: Identifiable, CustomStringConvertible {
177177
var id = UUID()
178178
var name: String
179-
var baseline: BenchmarkResult
180-
var latest: BenchmarkResult
181-
var type: ComparisonType = .runtime
179+
var baseline: Measurement
180+
var latest: Measurement
182181

183-
enum ComparisonType {
184-
case runtime
185-
case compileTime
186-
}
187-
188-
var latestTime: Time {
189-
switch type {
190-
case .compileTime:
191-
return latest.compileTime?.median ?? .zero
192-
case .runtime:
193-
return latest.runtime.median
194-
}
195-
}
196-
197-
var baselineTime: Time {
198-
switch type {
199-
case .compileTime:
200-
return baseline.compileTime?.median ?? .zero
201-
case .runtime:
202-
return baseline.runtime.median
203-
}
204-
}
205-
182+
var latestTime: Time { latest.median }
183+
var baselineTime: Time { baseline.median }
206184
var diff: Time? {
207-
switch type {
208-
case .compileTime:
185+
if Stats.tTest(baseline, latest) {
209186
return latestTime - baselineTime
210-
case .runtime:
211-
if Stats.tTest(baseline.runtime, latest.runtime) {
212-
return latestTime - baselineTime
213-
}
214-
return nil
215187
}
188+
return nil
216189
}
217-
218190
var normalizedDiff: Double {
219191
latestTime.seconds/baselineTime.seconds
220192
}
@@ -248,10 +220,11 @@ struct SuiteResult {
248220

249221
func compare(with other: SuiteResult) -> [BenchmarkResult.Comparison] {
250222
var comparisons: [BenchmarkResult.Comparison] = []
251-
for item in results {
252-
if let otherVal = other.results[item.key] {
223+
for latest in results {
224+
if let otherVal = other.results[latest.key] {
253225
comparisons.append(
254-
.init(name: item.key, baseline: otherVal, latest: item.value))
226+
.init(name: latest.key,
227+
baseline: otherVal.runtime, latest: latest.value.runtime))
255228
}
256229
}
257230
return comparisons
@@ -260,11 +233,12 @@ struct SuiteResult {
260233
/// Compares with the NSRegularExpression benchmarks generated by CrossBenchmark
261234
func compareWithNS() -> [BenchmarkResult.Comparison] {
262235
var comparisons: [BenchmarkResult.Comparison] = []
263-
for item in results {
264-
let key = item.key + CrossBenchmark.nsSuffix
236+
for latest in results {
237+
let key = latest.key + CrossBenchmark.nsSuffix
265238
if let nsResult = results[key] {
266239
comparisons.append(
267-
.init(name: item.key, baseline: nsResult, latest: item.value))
240+
.init(name: latest.key,
241+
baseline: nsResult.runtime, latest: latest.value.runtime))
268242
}
269243
}
270244
return comparisons
@@ -274,13 +248,14 @@ struct SuiteResult {
274248
with other: SuiteResult
275249
) -> [BenchmarkResult.Comparison] {
276250
var comparisons: [BenchmarkResult.Comparison] = []
277-
for item in results {
278-
if let otherVal = other.results[item.key] {
251+
for latest in results {
252+
if let baseline = other.results[latest.key],
253+
let baselineTime = baseline.compileTime,
254+
let latestTime = latest.value.compileTime {
279255
comparisons.append(
280-
.init(name: item.key,
281-
baseline: otherVal,
282-
latest: item.value,
283-
type: .compileTime))
256+
.init(name: latest.key,
257+
baseline: baselineTime,
258+
latest: latestTime))
284259
}
285260
}
286261
return comparisons

Sources/RegexBenchmark/BenchmarkRunner.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ struct BenchmarkRunner {
8181
print("Running")
8282
for b in suite {
8383
var result = measure(benchmark: b, samples: samples)
84-
if result.runtime.stdev > Stats.maxAllowedStdev {
85-
print("Warning: Standard deviation > \(Time(Stats.maxAllowedStdev)) for \(b.name)")
84+
if result.runtime.stdev > Stats.maxAllowedStdev * result.runtime.median.seconds {
85+
print("Warning: Standard deviation > \(Stats.maxAllowedStdev*100)% for \(b.name)")
8686
print(result.runtime)
8787
print("Rerunning \(b.name)")
8888
result = measure(benchmark: b, samples: result.runtime.samples*2)

Sources/RegexBenchmark/Utils/Stats.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import Foundation
33
enum Stats {}
44

55
extension Stats {
6-
// 500µs, maybe this should be a % of the runtime for each benchmark?
7-
static let maxAllowedStdev = 500e-6
6+
// Maximum allowed standard deviation is 5% of the median runtime
7+
static let maxAllowedStdev = 0.05
88

99
static func tTest(_ a: Measurement, _ b: Measurement) -> Bool {
1010
// Student's t-test

0 commit comments

Comments
 (0)