Skip to content

More benchmarker features #595

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
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
24 changes: 20 additions & 4 deletions Sources/RegexBenchmark/Benchmark.swift
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import _StringProcessing
@_spi(RegexBenchmark) import _StringProcessing
import Foundation

protocol RegexBenchmark {
var name: String { get }
func compile()
func run()
func debug()
}
Expand All @@ -19,6 +20,10 @@ struct Benchmark: RegexBenchmark {
case allMatches
}

func compile() {
blackHole(regex._compileRegex())
}

func run() {
switch type {
case .whole: blackHole(target.wholeMatch(of: regex))
Expand All @@ -43,6 +48,8 @@ struct NSBenchmark: RegexBenchmark {
case first
}

func compile() {}

func run() {
switch type {
case .allMatches: blackHole(regex.matches(in: target, range: range))
Expand All @@ -57,6 +64,10 @@ struct InputListBenchmark: RegexBenchmark {
let regex: Regex<AnyRegexOutput>
let targets: [String]

func compile() {
blackHole(regex._compileRegex())
}

func run() {
for target in targets {
blackHole(target.wholeMatch(of: regex))
Expand All @@ -79,6 +90,8 @@ struct InputListNSBenchmark: RegexBenchmark {
NSRange(target.startIndex..<target.endIndex, in: target)
}

func compile() {}

func run() {
for target in targets {
let range = range(in: target)
Expand All @@ -89,6 +102,9 @@ struct InputListNSBenchmark: RegexBenchmark {

/// A benchmark meant to be ran across multiple engines
struct CrossBenchmark {
/// Suffix added onto NSRegularExpression benchmarks
static let nsSuffix = "_NS"

/// The base name of the benchmark
var baseName: String

Expand Down Expand Up @@ -127,7 +143,7 @@ struct CrossBenchmark {
target: input))
runner.register(
NSBenchmark(
name: baseName + "Whole_NS",
name: baseName + "Whole" + CrossBenchmark.nsSuffix,
regex: nsRegex,
type: .first,
target: input))
Expand All @@ -140,7 +156,7 @@ struct CrossBenchmark {
target: input))
runner.register(
NSBenchmark(
name: baseName + "All_NS",
name: baseName + "All" + CrossBenchmark.nsSuffix,
regex: nsRegex,
type: .allMatches,
target: input))
Expand All @@ -153,7 +169,7 @@ struct CrossBenchmark {
target: input))
runner.register(
NSBenchmark(
name: baseName + "First_NS",
name: baseName + "First" + CrossBenchmark.nsSuffix,
regex: nsRegex,
type: .first,
target: input))
Expand Down
81 changes: 81 additions & 0 deletions Sources/RegexBenchmark/BenchmarkChart.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2021-2022 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
//
//===----------------------------------------------------------------------===//

#if os(macOS)

import Charts
import SwiftUI

struct BenchmarkChart: View {
var comparisons: [BenchmarkResult.Comparison]

var sortedComparisons: [BenchmarkResult.Comparison] {
comparisons.sorted { a, b in
a.latest.median.seconds/a.baseline.median.seconds <
b.latest.median.seconds/b.baseline.median.seconds
}
}
var body: some View {
VStack(alignment: .leading) {
Chart {
ForEach(sortedComparisons) { comparison in
let new = comparison.latest.median.seconds
let old = comparison.baseline.median.seconds
chartBody(
name: comparison.name,
new: new,
old: old,
sampleCount: comparison.latest.samples)
}
// Baseline
RuleMark(y: .value("Time", 1.0))
.foregroundStyle(.red)
.lineStyle(.init(lineWidth: 1, dash: [2]))

}.frame(idealHeight: 400)
}
}

@ChartContentBuilder
func chartBody(
name: String,
new: TimeInterval,
old: TimeInterval,
sampleCount: Int
) -> some ChartContent {
// Normalized runtime
BarMark(
x: .value("Name", name),
y: .value("Normalized runtime", new / old))
.foregroundStyle(LinearGradient(
colors: [.accentColor, new - old <= 0 ? .green : .yellow],
startPoint: .bottom,
endPoint: .top))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could inline this into the ForEach { ... }. I pulled this out to chartBody because of "expression too complex". That doesn't happen with just one bar mark.

}
}

struct BenchmarkResultApp: App {
static var comparisons: [BenchmarkResult.Comparison]?

var body: some Scene {
WindowGroup {
if let comparisons = Self.comparisons {
ScrollView {
BenchmarkChart(comparisons: comparisons)
}
} else {
Text("No data")
}
}
}
}

#endif
Loading