-
Notifications
You must be signed in to change notification settings - Fork 6
/
Copy pathBenchmarkIPC.swift
155 lines (138 loc) · 6.11 KB
/
BenchmarkIPC.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// Copyright © 2017 Károly Lőrentey.
// This file is part of Attabench: https://github.com/attaswift/Benchmarking
// For licensing information, see the file LICENSE.md in the Git repository above.
import Foundation
/// The options available for running benchmarks.
public struct RunOptions: Codable {
/// Represents an output format for the command line interface.
/// Note that attabench internal IPC is always encoded in JSON; this option is ignored for that.
public enum OutputFormat: String, Codable {
case pretty
case json
}
/// The tasks to run.
public var tasks: [String]
/// The sizes on which to run tasks.
public var sizes: [Int]
/// Output format for the command line interface. (Ignored for internal IPC.)
public var outputFormat: OutputFormat
/// The number of times to run each task/size measurement.
/// The reported result is the minimum elapsed time across all iterations.
/// (Depending on how slow/fast the task is, this count may get overridden by `minimumDuration` and/or `maximumDuration`.)
public var iterations: Int
/// Repeat each particular task/size measurement until at least this amount of time has passed.
/// (When this is positive, tasks may get run for more than `iterations` times.)
public var minimumDuration: Double?
/// Stop repeating a particular task/size measurement after this amount of time has passed.
/// (When this is finite, tasks may get run for less than `iterations` times.)
public var maximumDuration: Double?
public init(tasks: [String] = [],
sizes: [Int] = [],
outputFormat: OutputFormat = .pretty,
iterations: Int = 3,
minimumDuration: Double? = nil,
maximumDuration: Double? = nil) {
self.tasks = tasks
self.sizes = sizes
self.outputFormat = outputFormat
self.iterations = iterations
self.minimumDuration = minimumDuration
self.maximumDuration = maximumDuration
}
}
/// The benchmarking operation to perform.
/// This is encoded as JSON, and then sent to the standard input of the benchmark process.
public enum Command: Codable {
/// Write available tasks to the report file, then exit immediately.
case list
/// Start running the specified measurements indefinitely.
/// The benchmark process is expected to write a `RunProgressReport` value to the report file before/after
/// each successful measurement.
/// When enough measurements have been collected, the benchmark runner sends a SIGTERM signal to the
/// benchmark process, which is expected to exit within 2 seconds after receiving it.
/// If the benchmark process fails to exit by this deadline, the benchmark runner forcibly kills it with SIGKILL.
case run(RunOptions)
enum Code: String, Codable {
case list
case run
}
enum Key: String, CodingKey {
case command
case parameters
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
switch try container.decode(Code.self, forKey: .command) {
case .list:
self = .list
case .run:
self = .run(try container.decode(RunOptions.self, forKey: .parameters))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
switch self {
case .list:
try container.encode(Code.list, forKey: .command)
case .run(let options):
try container.encode(Code.run, forKey: .command)
try container.encode(options, forKey: .parameters)
}
}
}
/// Information about the execution state and results of a running benchmark.
/// Benchmark processes are expected to report their progress by encoding instances of this type
/// as JSON to their report file.
public enum Report: Codable {
/// The list of available tasks. (The answer for a `.list` command.)
/// The process should exit on its own after sending this report.
case list(tasks: [String])
/// A new measurement is starting to execute.
case begin(task: String, size: Int)
/// A task has been successfully benchmarked at the specified size.
/// The reported time is the minimum of all individual measurements.
case finish(task: String, size: Int, time: Double)
enum Key: String, CodingKey {
case code
case tasks
case task
case size
case time
}
enum Code: String, Codable {
case list
case begin
case finish
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
switch try container.decode(Code.self, forKey: .code) {
case .list:
self = .list(tasks: try container.decode([String].self, forKey: .tasks))
case .begin:
self = .begin(task: try container.decode(String.self, forKey: .task),
size: try container.decode(Int.self, forKey: .size))
case .finish:
self = .finish(task: try container.decode(String.self, forKey: .task),
size: try container.decode(Int.self, forKey: .size),
time: try container.decode(Double.self, forKey: .time))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
switch self {
case .list(tasks: let tasks):
try container.encode(Code.list, forKey: .code)
try container.encode(tasks, forKey: .tasks)
case let .begin(task: task, size: size):
try container.encode(Code.begin, forKey: .code)
try container.encode(task, forKey: .task)
try container.encode(size, forKey: .size)
case let .finish(task: task, size: size, time: time):
try container.encode(Code.finish, forKey: .code)
try container.encode(task, forKey: .task)
try container.encode(size, forKey: .size)
try container.encode(time, forKey: .time)
}
}
}