Skip to content

Commit

Permalink
feat: BenchmarkExportConfigurations
Browse files Browse the repository at this point in the history
  • Loading branch information
Jairon Terrero authored and CrownedPhoenix committed Dec 5, 2024
1 parent 6ff98fa commit 0d5da64
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 0 deletions.
5 changes: 5 additions & 0 deletions Sources/Benchmark/Benchmark.swift
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,8 @@ public extension Benchmark {
public var skip = false
/// Customized threshold tolerances for a given metric for the Benchmark used for checking for regressions/improvements/equality.
public var thresholds: [BenchmarkMetric: BenchmarkThresholds]?
/// Benchmark specific configurations to be provided to the exporters
public var exportConfigurations: BenchmarkExportConfigurations?
/// Optional per-benchmark specific setup done before warmup and all iterations
public var setup: BenchmarkSetupHook?
/// Optional per-benchmark specific teardown done after final run is done
Expand All @@ -419,6 +421,7 @@ public extension Benchmark {
skip: Bool = defaultConfiguration.skip,
thresholds: [BenchmarkMetric: BenchmarkThresholds]? =
defaultConfiguration.thresholds,
exportConfigurations: [BenchmarkExportConfigurationKey: any BenchmarkExportConfiguration] = [:],
setup: BenchmarkSetupHook? = nil,
teardown: BenchmarkTeardownHook? = nil) {
self.metrics = metrics
Expand All @@ -430,6 +433,7 @@ public extension Benchmark {
self.maxIterations = maxIterations
self.skip = skip
self.thresholds = thresholds
self.exportConfigurations = BenchmarkExportConfigurations(configs: exportConfigurations)
self.setup = setup
self.teardown = teardown
}
Expand All @@ -444,6 +448,7 @@ public extension Benchmark {
case maxDuration
case maxIterations
case thresholds
case exportConfigurations
}
// swiftlint:enable nesting
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/// A configuration used or expected by a particular result exporter
public protocol BenchmarkExportConfiguration: Codable {}

public struct BenchmarkExportConfigurationKey: Hashable, Codable {
private let value: String
}

/// The set of export configurations for a particular benchmark
public struct BenchmarkExportConfigurations: Codable {
let configs: [BenchmarkExportConfigurationKey: any BenchmarkExportConfiguration]

public init(configs: [BenchmarkExportConfigurationKey: any BenchmarkExportConfiguration]) {
self.configs = configs
}

public subscript(_ key: BenchmarkExportConfigurationKey) -> (any BenchmarkExportConfiguration)? {
configs[key]
}
}

extension BenchmarkExportConfigurations: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (BenchmarkExportConfigurationKey, any BenchmarkExportConfiguration)...) {
configs = Dictionary(elements, uniquingKeysWith: { $1 })
}
}

// N.B. We are clever with the codability implementation below
// since the value type in `BenchmarkExportConfigurations` is
// an existential type.
// The key mechanism is `BenchmarkExportConfigurationKey.resolveConfigType`
// that enables us to determine the appropriate concrete type to
// attempt to decode based on the key names located in the
// data container.

extension BenchmarkExportConfigurationKey {
/// This is used to determine the concrete type to attempt
/// to decode for a particular ``BenchmarkExportConfigurationKey``
static func resolveConfigType(from key: Self) -> BenchmarkExportConfiguration.Type? {
switch key {
// Add a case here when adding a new exporter config
default: nil
}
}
}

public extension BenchmarkExportConfigurations {
init(from decoder: any Decoder) throws {
let container = try decoder.container(keyedBy: BenchmarkExportConfigurationKey.self)
self.configs = try container.allKeys.reduce(
into: [BenchmarkExportConfigurationKey: any BenchmarkExportConfiguration]()
) { configs, key in
if let configType = type(of: key).resolveConfigType(from: key) {
configs[key] = try container.decode(configType.self, forKey: key)
}
}
}

func encode(to encoder: any Encoder) throws {
var encoder = encoder.container(keyedBy: BenchmarkExportConfigurationKey.self)
for (key, config) in configs {
try encoder.encode(config, forKey: key)
}
}
}

extension BenchmarkExportConfigurationKey: CodingKey {
public var stringValue: String { value }

public init?(stringValue: String) { self.init(value: stringValue) }

public var intValue: Int? { nil }

public init?(intValue: Int) { nil }
}

0 comments on commit 0d5da64

Please sign in to comment.