231 lines
6.6 KiB
Swift
231 lines
6.6 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift Baggage Context open source project
|
|
//
|
|
// Copyright (c) 2020 Moritz Lang and the Swift Baggage Context project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// Based on: https://github.com/apple/swift/tree/cf53143a47278c2a465409a67376642515956777/benchmark/utils
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#if os(Linux)
|
|
import Glibc
|
|
#else
|
|
import Darwin
|
|
#endif
|
|
|
|
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) -> Void
|
|
|
|
/// A function that invokes the specific benchmark routine.
|
|
public var runFunction: ((Int) -> Void)? {
|
|
if !self.shouldRun {
|
|
return nil
|
|
}
|
|
return self._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: (() -> Void)?
|
|
|
|
/// An optional function that if non-null is run before benchmark samples
|
|
/// are timed.
|
|
public var setUpFunction: (() -> Void)? {
|
|
if !self.shouldRun {
|
|
return nil
|
|
}
|
|
return self._setUpFunction
|
|
}
|
|
|
|
/// Shadow static variable for computed property tearDownFunction.
|
|
private var _tearDownFunction: (() -> Void)?
|
|
|
|
/// An optional function that if non-null is run after samples are taken.
|
|
public var tearDownFunction: (() -> Void)? {
|
|
if !self.shouldRun {
|
|
return nil
|
|
}
|
|
return self._tearDownFunction
|
|
}
|
|
|
|
public var legacyFactor: Int?
|
|
|
|
public init(
|
|
name: String, runFunction: @escaping (Int) -> Void, tags: [BenchmarkCategory],
|
|
setUpFunction: (() -> Void)? = nil,
|
|
tearDownFunction: (() -> Void)? = 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 !self.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(self.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 = 0xB789_78E7
|
|
|
|
mutating func shift() {
|
|
self.lfsr = (self.lfsr >> 1) ^ (UInt32(bitPattern: -Int32(self.lfsr & 1)) & 0xD000_0001)
|
|
}
|
|
|
|
mutating func randInt() -> Int64 {
|
|
var result: UInt32 = 0
|
|
for _ in 0 ..< 32 {
|
|
result = (result << 1) | (self.lfsr & 1)
|
|
self.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()
|
|
}
|
|
|
|
@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()
|
|
}
|
|
}
|
|
|
|
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>(_: 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 }
|