Skip to content

Commit 533b3a1

Browse files
committed
Benchmark: option to output perf as csv
1 parent a847713 commit 533b3a1

File tree

3 files changed

+66
-2
lines changed

3 files changed

+66
-2
lines changed

Sources/RegexBenchmark/BenchmarkResults.swift

+49-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,21 @@ extension BenchmarkRunner {
2121
self.results = result
2222
print("Loaded results from \(url.path)")
2323
}
24-
24+
25+
/// Attempts to save results in a CSV format to the given path
26+
func saveCSV(to savePath: String) throws {
27+
let url = URL(fileURLWithPath: savePath, isDirectory: false)
28+
let parent = url.deletingLastPathComponent()
29+
if !FileManager.default.fileExists(atPath: parent.path) {
30+
try! FileManager.default.createDirectory(
31+
atPath: parent.path,
32+
withIntermediateDirectories: true)
33+
}
34+
print("Saving result as CSV to \(url.path)")
35+
try results.saveCSV(to: url)
36+
37+
}
38+
2539
/// Compare this runner's results against the results stored in the given file path
2640
func compare(
2741
against compareFilePath: String,
@@ -153,6 +167,12 @@ struct Measurement: Codable, CustomStringConvertible {
153167
var description: String {
154168
return "\(median) (stdev: \(Time(stdev)), N = \(samples))"
155169
}
170+
171+
var asCSV: String {
172+
"""
173+
\(median.asCSVSeconds), \(stdev), \(samples)
174+
"""
175+
}
156176
}
157177

158178
struct BenchmarkResult: Codable, CustomStringConvertible {
@@ -170,6 +190,13 @@ struct BenchmarkResult: Codable, CustomStringConvertible {
170190
}
171191
return base
172192
}
193+
194+
var asCSV: String {
195+
let na = "N/A, N/A, N/A"
196+
return """
197+
\(runtime.asCSV), \(compileTime?.asCSV ?? na), \(parseTime?.asCSV ?? na)
198+
"""
199+
}
173200
}
174201

175202
extension BenchmarkResult {
@@ -263,6 +290,27 @@ struct SuiteResult {
263290
}
264291

265292
extension SuiteResult: Codable {
293+
func saveCSV(to url: URL) throws {
294+
var output: [(name: String, result: BenchmarkResult)] = []
295+
for key in results.keys {
296+
output.append((key, results[key]!))
297+
}
298+
output.sort {
299+
$0.name < $1.name
300+
}
301+
var contents = """
302+
name,\
303+
runtime_median, runTime_stddev, runTime_samples,\
304+
compileTime_median, compileTime_stddev, compileTime_samples,\
305+
parseTime_median, parseTime_stddev, parseTime_samples\n
306+
"""
307+
for (name, result) in output {
308+
contents.append("\(name), \(result.asCSV))\n")
309+
}
310+
print("Saving result as .csv to \(url.path())")
311+
try contents.write(to: url, atomically: true, encoding: String.Encoding.utf8)
312+
}
313+
266314
func save(to url: URL) throws {
267315
let encoder = JSONEncoder()
268316
let data = try encoder.encode(self)

Sources/RegexBenchmark/CLI.swift

+12-1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ struct Runner: ParsableCommand {
3232
@Option(help: "Save comparison results as csv")
3333
var saveComparison: String?
3434

35+
@Option(help: "Save benchmark results as csv")
36+
var saveCSV: String?
37+
3538
@Flag(help: "Quiet mode")
3639
var quiet = false
3740

@@ -91,9 +94,14 @@ swift build -c release -Xswiftc -DPROCESSOR_MEASUREMENTS_ENABLED
9194

9295
if let loadFile = load {
9396
try runner.load(from: loadFile)
97+
if excludeNs {
98+
runner.results.results = runner.results.results.filter {
99+
!$0.key.contains("_NS")
100+
}
101+
}
94102
} else {
95103
if excludeNs {
96-
runner.suite = runner.suite.filter { b in !b.name.contains("NS") }
104+
runner.suite = runner.suite.filter { b in !b.name.contains("_NS") }
97105
}
98106
runner.run()
99107
}
@@ -116,5 +124,8 @@ swift build -c release -Xswiftc -DPROCESSOR_MEASUREMENTS_ENABLED
116124
if let compareFile = compareCompileTime {
117125
try runner.compareCompileTimes(against: compareFile, showChart: showChart)
118126
}
127+
if let csvPath = saveCSV {
128+
try runner.saveCSV(to: csvPath)
129+
}
119130
}
120131
}

Sources/RegexBenchmark/Utils/Time.swift

+5
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,11 @@ extension Time {
6666
}
6767

6868
extension Time: CustomStringConvertible {
69+
/// Normalize our time to fractions of a second for CSV output
70+
public var asCSVSeconds: String {
71+
return String(format: "%.3g", seconds)
72+
}
73+
6974
public var description: String {
7075
if self.seconds == 0 { return "0" }
7176
if self.abs() < .attosecond { return String(format: "%.3gas", seconds * 1e18) }

0 commit comments

Comments
 (0)