forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
/
TestsUtils.swift
327 lines (281 loc) · 10.9 KB
/
TestsUtils.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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
//===--- TestsUtils.swift -------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#if os(Linux)
import Glibc
#elseif os(Windows)
import MSVCRT
#else
import Darwin
#endif
public enum BenchmarkCategory : String {
// Validation "micro" benchmarks test a specific operation or critical path that
// we know is important to measure.
case validation
// subsystems to validate and their subcategories.
case api, Array, String, Dictionary, Codable, Set, Data
case sdk
case runtime, refcount, metadata
// Other general areas of compiled code validation.
case abstraction, safetychecks, exceptions, bridging, concurrency, existential
case exclusivity
// Algorithms are "micro" that test some well-known algorithm in isolation:
// sorting, searching, hashing, fibonaci, crypto, etc.
case algorithm
// Miniapplications are contrived to mimic some subset of application behavior
// in a way that can be easily measured. They are larger than micro-benchmarks,
// combining multiple APIs, data structures, or algorithms. This includes small
// standardized benchmarks, pieces of real applications that have been extracted
// into a benchmark, important functionality like JSON parsing, etc.
case miniapplication
// Regression benchmarks is a catch-all for less important "micro"
// benchmarks. This could be a random piece of code that was attached to a bug
// report. We want to make sure the optimizer as a whole continues to handle
// this case, but don't know how applicable it is to general Swift performance
// relative to the other micro-benchmarks. In particular, these aren't weighted
// as highly as "validation" benchmarks and likely won't be the subject of
// future investigation unless they significantly regress.
case regression
// Most benchmarks are assumed to be "stable" and will be regularly tracked at
// each commit. A handful may be marked unstable if continually tracking them is
// counterproductive.
case unstable
// CPU benchmarks represent instrinsic Swift performance. They are useful for
// measuring a fully baked Swift implementation across different platforms and
// hardware. The benchmark should also be reasonably applicable to real Swift
// code--it should exercise a known performance critical area. Typically these
// will be drawn from the validation benchmarks once the language and standard
// library implementation of the benchmark meets a reasonable efficiency
// baseline. A benchmark should only be tagged "cpubench" after a full
// performance investigation of the benchmark has been completed to determine
// that it is a good representation of future Swift performance. Benchmarks
// should not be tagged if they make use of an API that we plan on
// reimplementing or call into code paths that have known opportunities for
// significant optimization.
case cpubench
// Explicit skip marker
case skip
}
extension BenchmarkCategory : CustomStringConvertible {
public var description: String {
return self.rawValue
}
}
extension BenchmarkCategory : Comparable {
public static func < (lhs: BenchmarkCategory, rhs: BenchmarkCategory) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
public struct BenchmarkPlatformSet : OptionSet {
public let rawValue: Int
public init(rawValue: Int) {
self.rawValue = rawValue
}
public static let darwin = BenchmarkPlatformSet(rawValue: 1 << 0)
public static let linux = BenchmarkPlatformSet(rawValue: 1 << 1)
public static var currentPlatform: BenchmarkPlatformSet {
#if os(Linux)
return .linux
#else
return .darwin
#endif
}
public static var allPlatforms: BenchmarkPlatformSet {
return [.darwin, .linux]
}
}
public struct BenchmarkInfo {
/// The name of the benchmark that should be displayed by the harness.
public var name: String
/// Shadow static variable for runFunction.
private var _runFunction: (Int) -> ()
/// A function that invokes the specific benchmark routine.
public var runFunction: ((Int) -> ())? {
if !shouldRun {
return nil
}
return _runFunction
}
/// A set of category tags that describe this benchmark. This is used by the
/// harness to allow for easy slicing of the set of benchmarks along tag
/// boundaries, e.x.: run all string benchmarks or ref count benchmarks, etc.
public var tags: Set<BenchmarkCategory>
/// The platforms that this benchmark supports. This is an OptionSet.
private var unsupportedPlatforms: BenchmarkPlatformSet
/// Shadow variable for setUpFunction.
private var _setUpFunction: (() -> ())?
/// An optional function that if non-null is run before benchmark samples
/// are timed.
public var setUpFunction : (() -> ())? {
if !shouldRun {
return nil
}
return _setUpFunction
}
/// Shadow static variable for computed property tearDownFunction.
private var _tearDownFunction: (() -> ())?
/// An optional function that if non-null is run after samples are taken.
public var tearDownFunction: (() -> ())? {
if !shouldRun {
return nil
}
return _tearDownFunction
}
/// DON'T USE ON NEW BENCHMARKS!
/// Optional `legacyFactor` is a multiplication constant applied to runtime
/// statistics reported in the benchmark summary (it doesn’t affect the
/// individual sample times reported in `--verbose` mode).
///
/// It enables the migration of benchmark suite to smaller workloads (< 1 ms),
/// which are more robust to measurement errors from system under load,
/// while maintaining the continuity of longterm benchmark tracking.
///
/// Most legacy benchmarks had workloads artificially inflated in their main
/// `for` loops with a constant integer factor and the migration consisted of
/// dividing it so that the optimized runtime (-O) was less than 1000 μs and
/// storing the divisor in `legacyFactor`. This effectively only increases the
/// frequency of measurement, gathering more samples that are much less likely
/// to be interrupted by a context switch.
public var legacyFactor: Int?
public init(name: String, runFunction: @escaping (Int) -> (), tags: [BenchmarkCategory],
setUpFunction: (() -> ())? = nil,
tearDownFunction: (() -> ())? = nil,
unsupportedPlatforms: BenchmarkPlatformSet = [],
legacyFactor: Int? = nil) {
self.name = name
self._runFunction = runFunction
self.tags = Set(tags)
self._setUpFunction = setUpFunction
self._tearDownFunction = tearDownFunction
self.unsupportedPlatforms = unsupportedPlatforms
self.legacyFactor = legacyFactor
}
/// Returns true if this benchmark should be run on the current platform.
var shouldRun: Bool {
return !unsupportedPlatforms.contains(.currentPlatform)
}
}
extension BenchmarkInfo : Comparable {
public static func < (lhs: BenchmarkInfo, rhs: BenchmarkInfo) -> Bool {
return lhs.name < rhs.name
}
public static func == (lhs: BenchmarkInfo, rhs: BenchmarkInfo) -> Bool {
return lhs.name == rhs.name
}
}
extension BenchmarkInfo : Hashable {
public func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
// Linear function shift register.
//
// This is just to drive benchmarks. I don't make any claim about its
// strength. According to Wikipedia, it has the maximal period for a
// 32-bit register.
struct LFSR {
// Set the register to some seed that I pulled out of a hat.
var lfsr : UInt32 = 0xb78978e7
mutating func shift() {
lfsr = (lfsr >> 1) ^ (UInt32(bitPattern: -Int32((lfsr & 1))) & 0xD0000001)
}
mutating func randInt() -> Int64 {
var result : UInt32 = 0
for _ in 0..<32 {
result = (result << 1) | (lfsr & 1)
shift()
}
return Int64(bitPattern: UInt64(result))
}
}
var lfsrRandomGenerator = LFSR()
// Start the generator from the beginning
public func SRand() {
lfsrRandomGenerator = LFSR()
}
public func Random() -> Int64 {
return lfsrRandomGenerator.randInt()
}
// This is a fixed-increment version of Java 8's SplittableRandom generator.
// It is a very fast generator passing BigCrush, with 64 bits of state.
// See http://dx.doi.org/10.1145/2714064.2660195 and
// http://docs.oracle.com/javase/8/docs/api/java/util/SplittableRandom.html
//
// Derived from public domain C implementation by Sebastiano Vigna
// See http://xoshiro.di.unimi.it/splitmix64.c
public struct SplitMix64: RandomNumberGenerator {
private var state: UInt64
public init(seed: UInt64) {
self.state = seed
}
public mutating func next() -> UInt64 {
self.state &+= 0x9e3779b97f4a7c15
var z: UInt64 = self.state
z = (z ^ (z &>> 30)) &* 0xbf58476d1ce4e5b9
z = (z ^ (z &>> 27)) &* 0x94d049bb133111eb
return z ^ (z &>> 31)
}
}
@inlinable // FIXME(inline-always)
@inline(__always)
public func CheckResults(
_ resultsMatch: Bool,
file: StaticString = #file,
function: StaticString = #function,
line: Int = #line
) {
guard _fastPath(resultsMatch) else {
print("Incorrect result in \(function), \(file):\(line)")
abort()
}
}
#if !_runtime(_ObjC)
// If we do not have an objc-runtime, then we do not have a definition for
// autoreleasepool. Add in our own fake autoclosure for it that is inline
// always. That should be able to be eaten through by the optimizer no problem.
@inlinable // FIXME(inline-always)
@inline(__always)
public func autoreleasepool<Result>(
invoking body: () throws -> Result
) rethrows -> Result {
return try body()
}
#endif
public func False() -> Bool { return false }
/// This is a dummy protocol to test the speed of our protocol dispatch.
public protocol SomeProtocol { func getValue() -> Int }
struct MyStruct : SomeProtocol {
init() {}
func getValue() -> Int { return 1 }
}
public func someProtocolFactory() -> SomeProtocol { return MyStruct() }
// Just consume the argument.
// It's important that this function is in another module than the tests
// which are using it.
@inline(never)
public func blackHole<T>(_ x: T) {
}
// Return the passed argument without letting the optimizer know that.
@inline(never)
public func identity<T>(_ x: T) -> T {
return x
}
// Return the passed argument without letting the optimizer know that.
// It's important that this function is in another module than the tests
// which are using it.
@inline(never)
public func getInt(_ x: Int) -> Int { return x }
// The same for String.
@inline(never)
public func getString(_ s: String) -> String { return s }
// The same for Substring.
@inline(never)
public func getSubstring(_ s: Substring) -> Substring { return s }