Skip to content

Commit

Permalink
Report os_signposts on Darwin, split warmup reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
karwa committed Feb 9, 2022
1 parent 8163295 commit 6049e0c
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 12 deletions.
74 changes: 65 additions & 9 deletions Sources/Benchmark/BenchmarkReporter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@

import Foundation

#if canImport(OSLog)
import OSLog
#endif

protocol ProgressReporter {
mutating func report(running name: String, suite: String)
mutating func report(finishedRunning name: String, suite: String, nanosTaken: UInt64)
mutating func report(willBegin name: String, suite: String)

mutating func reportWarmingUp()
mutating func reportFinishedWarmup(nanosTaken: UInt64)

mutating func reportRunning()
mutating func reportFinishedRunning(nanosTaken: UInt64)
}

protocol BenchmarkReporter {
Expand All @@ -25,32 +34,79 @@ protocol BenchmarkReporter {

struct VerboseProgressReporter<Output: FlushableTextOutputStream>: ProgressReporter {
var output: Output
var prefix: String
var name: String
#if canImport(OSLog)
var currentOSLog: Any?
#endif

init(output: Output) {
self.output = output
self.prefix = ""
self.name = "<Benchmark Name>"
self.currentOSLog = nil
}

mutating func report(running name: String, suite: String) {
let prefix: String
mutating func report(willBegin name: String, suite: String) {
if suite != "" {
prefix = "\(suite): "
} else {
prefix = ""
}
print("running \(prefix)\(name)...", terminator: "", to: &output)
self.name = name
#if canImport(OSLog)
if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
currentOSLog = OSLog(subsystem: "\(prefix)\(name)", category: .pointsOfInterest)
}
#endif
}

mutating func reportWarmingUp() {
print("Warming up... ", terminator: "", to: &output)
output.flush()
#if canImport(OSLog)
if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
os_signpost(.begin, log: currentOSLog as! OSLog, name: "Warmup")
}
#endif
}

mutating func report(finishedRunning name: String, suite: String, nanosTaken: UInt64) {
mutating func reportFinishedWarmup(nanosTaken: UInt64) {
#if canImport(OSLog)
if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
os_signpost(.end, log: currentOSLog as! OSLog, name: "Warmup")
}
#endif
}

mutating func reportRunning() {
print("Running \(prefix)\(name)... ", terminator: "", to: &output)
output.flush()
#if canImport(OSLog)
if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
os_signpost(.begin, log: currentOSLog as! OSLog, name: "Benchmark")
}
#endif
}

mutating func reportFinishedRunning(nanosTaken: UInt64) {
let timeDuration = String(format: "%.2f ms", Float(nanosTaken) / 1000000.0)
print(" done! (\(timeDuration))", to: &output)
print("Done! (\(timeDuration))", to: &output)
output.flush()
#if canImport(OSLog)
if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) {
os_signpost(.end, log: currentOSLog as! OSLog, name: "Benchmark")
}
#endif
}
}

struct QuietReporter: ProgressReporter, BenchmarkReporter {
mutating func report(running name: String, suite: String) {}
mutating func report(finishedRunning name: String, suite: String, nanosTaken: UInt64) {}
mutating func report(willBegin name: String, suite: String) {}
mutating func reportWarmingUp() {}
mutating func reportFinishedWarmup(nanosTaken: UInt64) {}
mutating func reportRunning() {}
mutating func reportFinishedRunning(nanosTaken: UInt64) {}
mutating func report(results: [BenchmarkResult]) {}
}

Expand Down
10 changes: 7 additions & 3 deletions Sources/Benchmark/BenchmarkRunner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,20 @@ public struct BenchmarkRunner {
return
}

progress.report(running: benchmark.name, suite: suite.name)
progress.report(willBegin: benchmark.name, suite: suite.name)
let totalStart = now()

var warmupState: BenchmarkState? = nil
if settings.warmupIterations > 0 {
progress.reportWarmingUp()
warmupState = doNIterations(
settings.warmupIterations, benchmark: benchmark, suite: suite, settings: settings)
let warmupElapsed = warmupState!.endTime - warmupState!.startTime
progress.reportFinishedWarmup(nanosTaken: warmupElapsed)
}

progress.reportRunning()

var state: BenchmarkState
if let n = settings.iterations {
state = doNIterations(n, benchmark: benchmark, suite: suite, settings: settings)
Expand All @@ -105,8 +110,7 @@ public struct BenchmarkRunner {
let totalEnd = now()
let totalElapsed = totalEnd - totalStart

progress.report(
finishedRunning: benchmark.name, suite: suite.name, nanosTaken: totalElapsed)
progress.reportFinishedRunning(nanosTaken: totalElapsed)

let result = BenchmarkResult(
benchmarkName: benchmark.name,
Expand Down
1 change: 1 addition & 0 deletions Tests/BenchmarkTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ add_library(BenchmarkTests
BenchmarkSuiteTests.swift
CustomBenchmarkTests.swift
MockTextOutputStream.swift
ProgressReporterTests.swift
StatsTests.swift
XCTTestManifests.swift)

Expand Down
134 changes: 134 additions & 0 deletions Tests/BenchmarkTests/ProgressReporterTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import XCTest

@testable import Benchmark

final class ProgressReporterTests: XCTestCase {

fileprivate var dummySuite: BenchmarkSuite {
BenchmarkSuite(name: "SomeSuite") { suite in
suite.benchmark("SomeBenchmark") { /* No-op */ }
suite.benchmark("AnotherBenchmark") { /* No-op */ }
}
}

open class DummyProgressReporterBase: ProgressReporter {
var didReportWillBegin = false
var didReportWarmingUp = false
var didReportFinishedWarmup = false
var didReportRunning = false
var didReportFinishedRunning = false

init() {}

private func resetCallbackFlags() {
didReportWillBegin = false
didReportWarmingUp = false
didReportFinishedWarmup = false
didReportRunning = false
didReportFinishedRunning = false
}

open func report(willBegin name: String, suite: String) {
resetCallbackFlags()
didReportWillBegin = true
}

open func reportWarmingUp() {
didReportWarmingUp = true
}

open func reportFinishedWarmup(nanosTaken: UInt64) {
didReportFinishedWarmup = true
}

open func reportRunning() {
didReportRunning = true
}

open func reportFinishedRunning(nanosTaken: UInt64) {
didReportFinishedRunning = true
}
}

func testProgressReportNoWarmup() throws {

class Reporter: DummyProgressReporterBase {
var remainingBenchmarks = ["SomeBenchmark", "AnotherBenchmark"]

func checkFlags() {
if didReportWillBegin {
XCTAssertFalse(didReportWarmingUp)
XCTAssertFalse(didReportFinishedWarmup)
XCTAssertTrue(didReportRunning)
XCTAssertTrue(didReportFinishedRunning)
}
}

override func report(willBegin name: String, suite: String) {
// Check flags from the previous run, if there was one.
checkFlags()
// Reset the flags and check this run.
super.report(willBegin: name, suite: suite)
XCTAssertEqual(name, remainingBenchmarks.removeFirst())
XCTAssertEqual(suite, "SomeSuite")
}
}

let reporter = Reporter()
var runner = BenchmarkRunner(suites: [dummySuite], settings: [Iterations(5)])
runner.progress = reporter
try runner.run()
reporter.checkFlags()
}

func testProgressReportWithWarmup() throws {

class Reporter: DummyProgressReporterBase {
var remainingBenchmarks = ["SomeBenchmark", "AnotherBenchmark"]

func checkFlags() {
if didReportWillBegin {
XCTAssertTrue(didReportWarmingUp)
XCTAssertTrue(didReportFinishedWarmup)
XCTAssertTrue(didReportRunning)
XCTAssertTrue(didReportFinishedRunning)
}
}

override func report(willBegin name: String, suite: String) {
// Check flags from the previous run, if there was one.
checkFlags()
// Reset the flags and check this run.
super.report(willBegin: name, suite: suite)
XCTAssertEqual(name, remainingBenchmarks.removeFirst())
XCTAssertEqual(suite, "SomeSuite")
}
}

let reporter = Reporter()
var runner = BenchmarkRunner(
suites: [dummySuite], settings: [WarmupIterations(420), Iterations(5)])
runner.progress = reporter
try runner.run()
reporter.checkFlags()
}

static var allTests = [
("testProgressReportNoWarmup", testProgressReportNoWarmup),
("testProgressReportWithWarmup", testProgressReportWithWarmup),
]
}
1 change: 1 addition & 0 deletions Tests/BenchmarkTests/XCTTestManifests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import XCTest
testCase(BenchmarkSettingTests.allTests),
testCase(BenchmarkSuiteTests.allTests),
testCase(CustomBenchmarkTests.allTests),
testCase(ProgressReporterTests.allTests),
testCase(StatsTests.allTests),
]
}
Expand Down

0 comments on commit 6049e0c

Please sign in to comment.