Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bin/benchmark/Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version: 5.5
// swift-tools-version: 6.0
/*
This source file is part of the Swift.org open source project

Expand All @@ -14,7 +14,7 @@ import PackageDescription
let package = Package(
name: "benchmark",
platforms: [
.macOS(.v12)
.macOS(.v13)
],
products: [
.executable(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ struct BenchmarkResultSeries: Codable, Equatable {
/// The list of metrics gathered in these benchmark runs.
public var metrics: [MetricSeries]

static var empty = BenchmarkResultSeries(platformName: "", timestamp: Date(), doccArguments: [], metrics: [])
static let empty = BenchmarkResultSeries(platformName: "", timestamp: Date(), doccArguments: [], metrics: [])

enum Error: Swift.Error, CustomStringConvertible {
case addedResultHasDifferentConfiguration
Expand Down
7 changes: 4 additions & 3 deletions bin/benchmark/Sources/benchmark/Commands.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,16 @@ import ArgumentParser
import Foundation

@main
struct BenchmarkCommand: ParsableCommand {
static var configuration = CommandConfiguration(
struct BenchmarkCommand: @MainActor ParsableCommand {
@MainActor
static let configuration = CommandConfiguration(
abstract: "A utility for performing benchmarks for Swift-DocC.",
subcommands: [Measure.self, Diff.self, CompareTo.self, MeasureCommits.self, RenderTrend.self],
defaultSubcommand: Measure.self)
}

let doccProjectRootURL: URL = {
let url = URL(fileURLWithPath: #file)
let url = URL(fileURLWithPath: #filePath)
.deletingLastPathComponent() // Commands.swift
.deletingLastPathComponent() // benchmark
.deletingLastPathComponent() // Sources
Expand Down
4 changes: 4 additions & 0 deletions bin/benchmark/Sources/benchmark/Diff/DiffAnalysis.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extension DiffResults {
}
}

@MainActor
static func analyze(before beforeMetric: BenchmarkResultSeries.MetricSeries?, after afterMetric: BenchmarkResultSeries.MetricSeries) throws -> DiffResults.MetricAnalysis {
guard let beforeMetric else {
return DiffResults.MetricAnalysis(
Expand Down Expand Up @@ -129,6 +130,7 @@ extension DiffResults {
)
}

@MainActor
private static func inputBiasDescription(metric: BenchmarkResultSeries.MetricSeries, sampleName: String, numbers: [Double]) -> String {
// Turn the single metric series into an array of single values metric series to render the trend bars.

Expand Down Expand Up @@ -164,6 +166,7 @@ extension DiffResults {
}

extension BenchmarkResultSeries.MetricSeries.ValueSeries {
@MainActor
func formatted() -> String {
switch self {
case .duration(let value):
Expand All @@ -190,6 +193,7 @@ extension BenchmarkResultSeries.MetricSeries.ValueSeries {
}

#if os(macOS) || os(iOS)
@MainActor
private let durationFormatter: MeasurementFormatter = {
let fmt = MeasurementFormatter()
fmt.unitStyle = .medium
Expand Down
63 changes: 46 additions & 17 deletions bin/benchmark/Sources/benchmark/Diff/DiffResultsTable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,51 @@
import Foundation

struct DiffResultsTable {
static var columns: [(name: String, width: Int)] = [
("Metric", 40),
("Change", 15),
("Before", 20),
("After", 20),
]
static var totalWidth: Int {
return columns.reduce(0, { $0 + $1.width + 3 }) - 1
struct Columns {
typealias Column = (name: String, width: Int)
var data: [Column]

init() {
data = [
("Metric", 40),
("Change", 15),
("Before", 20),
("After", 20),
]
}

var beforeInfo: Column {
get { data[2] }
set { data[2] = newValue }
}

var afterInfo: Column {
get { data[3] }
set { data[3] = newValue }
}

var totalWidth: Int {
data.reduce(0, { $0 + $1.width + 3 }) - 1
}

var names: [String] {
data.map { $0.name }
}
}

private(set) var output: String
init(results: DiffResults) {
init(results: DiffResults, columns: Columns) {
var output = ""

let allWarnings = results.analysis.flatMap { $0.warnings ?? [] }
for warning in allWarnings {
output += "\(warning)\n"
}

output += "┌\(String(repeating: "─", count: Self.totalWidth))┐\n"
output += Self.formattedRow(columnValues: Self.columns.map { $0.name })
output += "├\(String(repeating: "─", count: Self.totalWidth))┤\n"
let totalWidth = columns.totalWidth
output += "┌\(String(repeating: "─", count: totalWidth))┐\n"
output += Self.formattedRow(columns: columns)
output += "├\(String(repeating: "─", count: totalWidth))┤\n"

var footnoteCounter = 0

Expand Down Expand Up @@ -61,10 +84,16 @@ struct DiffResultsTable {
footnoteCounter += footnotes.count
}

output += Self.formattedRow(columnValues: [analysis.metricName, change, analysis.before ?? "-", analysis.after], colorInfo: colorInfo)
var analysisColumns = columns
analysisColumns.data[0].name = analysis.metricName
analysisColumns.data[1].name = change
analysisColumns.data[2].name = analysis.before ?? "-"
analysisColumns.data[3].name = analysis.after

output += Self.formattedRow(columns: analysisColumns, colorInfo: colorInfo)
}

output += "└\(String(repeating: "─", count: Self.totalWidth))┘\n"
output += "└\(String(repeating: "─", count: totalWidth))┘\n"

let allFootnotes = results.analysis.flatMap { $0.footnotes ?? [] }
if !allFootnotes.isEmpty {
Expand Down Expand Up @@ -117,9 +146,9 @@ struct DiffResultsTable {
let upTo: String.Index
}

private static func formattedRow(columnValues: [String], colorInfo: [ColumnColorInfo] = []) -> String {
let values: [String] = columnValues.enumerated().map { (index, value) in
let row = value.padding(toLength: Self.columns[index].width, withPad: " ", startingAt: 0)
private static func formattedRow(columns: Columns, colorInfo: [ColumnColorInfo] = []) -> String {
let values: [String] = columns.names.enumerated().map { (index, value) in
let row = value.padding(toLength: columns.data[index].width, withPad: " ", startingAt: 0)
if let colorInfo = colorInfo.first(where: { $0.index == index }) {
return String(row[..<colorInfo.upTo]).colored(colorInfo.color) + String(row[colorInfo.upTo...])
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import Foundation
import ArgumentParser
import SwiftDocC

struct CompareTo: ParsableCommand {
struct CompareTo: @MainActor ParsableCommand {
@Argument(
help: "The baseline 'commit-ish' to compare the current checkout against."
)
Expand All @@ -31,6 +31,7 @@ struct CompareTo: ParsableCommand {
return outputDirectory ?? URL(fileURLWithPath: ".") // fallback to the current directory
}

@MainActor
mutating func run() throws {
let currentDocCExecutable = try MeasureAction.buildDocC(at: doccProjectRootURL)
let currentBenchmarkResult = try MeasureAction.gatherMeasurements(
Expand Down
11 changes: 7 additions & 4 deletions bin/benchmark/Sources/benchmark/Subcommands/Diff.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import SwiftDocC

// For argument parsing and validation

struct Diff: ParsableCommand {
struct Diff: @MainActor ParsableCommand {
@Argument(
help: "The benchmark.json file to treat as the 'before' values in the diff.",
transform: { URL(fileURLWithPath: $0) }
Expand All @@ -34,6 +34,7 @@ struct Diff: ParsableCommand {
)
var jsonOutputFile: URL?

@MainActor
mutating func run() throws {
try DiffAction(
beforeFile: beforeFile,
Expand All @@ -50,6 +51,7 @@ struct DiffAction {
var afterFile: URL
var jsonOutputFile: URL?

@MainActor
func run() throws {
let beforeMetrics = try JSONDecoder().decode(BenchmarkResultSeries.self, from: Data(contentsOf: beforeFile)).metrics
let afterMetrics = try JSONDecoder().decode(BenchmarkResultSeries.self, from: Data(contentsOf: afterFile)).metrics
Expand All @@ -62,10 +64,11 @@ struct DiffAction {
try result.analysis.append(DiffResults.analyze(before: beforeMetric, after: afterMetric))
}

DiffResultsTable.columns[2] = tableColumnInfo(file: beforeFile)
DiffResultsTable.columns[3] = tableColumnInfo(file: afterFile)
var columns = DiffResultsTable.Columns()
columns.beforeInfo = tableColumnInfo(file: beforeFile)
columns.afterInfo = tableColumnInfo(file: afterFile)

let table = DiffResultsTable(results: result)
let table = DiffResultsTable(results: result, columns: columns)
print(table.output)

if let jsonOutputFile {
Expand Down
4 changes: 3 additions & 1 deletion bin/benchmark/Sources/benchmark/Subcommands/Measure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ struct MeasureOptions: ParsableCommand {
}
}

struct Measure: ParsableCommand {
struct Measure: @MainActor ParsableCommand {
@Option(
name: .customLong("output"),
help: "The path to write the output benchmark measurements file.",
Expand Down Expand Up @@ -93,6 +93,7 @@ struct Measure: ParsableCommand {
}
}

@MainActor
mutating func run() throws {
try MeasureAction(
repeatCount: measureOptions.repeatCount,
Expand All @@ -111,6 +112,7 @@ struct MeasureAction {
var doccConvertCommand: [String]
var computeMissingOutputSizeMetrics: Bool

@MainActor
func run() throws {
print("Building docc in release configuration".styled(.bold))
let doccURL = try Self.buildDocC(at: doccProjectRootURL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import SwiftDocC

// For argument parsing and validation

struct RenderTrend: ParsableCommand {
struct RenderTrend: @MainActor ParsableCommand {
@Option(
name: .customLong("filter"),
help: "A list metric IDs to render trends for. If no filters are specified, trends are rendered for all numeric metrics."
Expand All @@ -27,6 +27,7 @@ struct RenderTrend: ParsableCommand {
)
var benchmarkResults: [URL]

@MainActor
mutating func run() throws {
try RenderTrendAction(
metricFilters: metricFilters,
Expand All @@ -41,6 +42,7 @@ struct RenderTrendAction {
var metricFilters: [String]
var benchmarkResults: [URL]

@MainActor
func run() throws {
// Map the metric ID to the measured values across the benchmarks
var trendValues: [String: [BenchmarkResultSeries.MetricSeries]] = [:]
Expand Down Expand Up @@ -76,6 +78,7 @@ struct RenderTrendAction {
}
}

@MainActor
static func renderTrendBars(metrics: [BenchmarkResultSeries.MetricSeries]) -> String {
var output = ""

Expand Down