From bb0ae4ecf9fd5c9670777522b3f7c0931fb97511 Mon Sep 17 00:00:00 2001 From: Francesco Paolo Severino Date: Thu, 9 Oct 2025 23:11:19 +0200 Subject: [PATCH 1/4] Add Benchmarks for ConsoleLogger --- .github/workflows/benchmark.yml | 17 +++++++ Benchmarks/.gitignore | 1 + Benchmarks/.swift-format | 1 + .../ConsoleLoggerBenchmarks.swift | 47 +++++++++++++++++++ .../ConsoleLoggerBenchmarks/TestConsole.swift | 47 +++++++++++++++++++ Benchmarks/Package.swift | 46 ++++++++++++++++++ .../ConsoleLoggerBenchmarks.Logging.p90.json | 4 ++ 7 files changed, 163 insertions(+) create mode 100644 .github/workflows/benchmark.yml create mode 120000 Benchmarks/.gitignore create mode 120000 Benchmarks/.swift-format create mode 100644 Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift create mode 100644 Benchmarks/ConsoleLoggerBenchmarks/TestConsole.swift create mode 100644 Benchmarks/Package.swift create mode 100644 Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..ad9a52a5 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,17 @@ +name: benchmark +on: + workflow_dispatch: + inputs: + sha: + type: string + required: true + description: "The commit SHA to run the benchmarks against." + push: + branches: [main] + +jobs: + benchmark: + uses: vapor/ci/.github/workflows/run-benchmark.yml@main + with: + sha: ${{ inputs.sha }} + secrets: inherit \ No newline at end of file diff --git a/Benchmarks/.gitignore b/Benchmarks/.gitignore new file mode 120000 index 00000000..5a19b83f --- /dev/null +++ b/Benchmarks/.gitignore @@ -0,0 +1 @@ +../.gitignore \ No newline at end of file diff --git a/Benchmarks/.swift-format b/Benchmarks/.swift-format new file mode 120000 index 00000000..18231f5a --- /dev/null +++ b/Benchmarks/.swift-format @@ -0,0 +1 @@ +../.swift-format \ No newline at end of file diff --git a/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift b/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift new file mode 100644 index 00000000..9cc7343f --- /dev/null +++ b/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift @@ -0,0 +1,47 @@ +import Benchmark +import ConsoleKit +import ConsoleLogger +import Logging + +let benchmarks: @Sendable () -> Void = { + Benchmark.defaultConfiguration = .init( + metrics: [.peakMemoryResident, .mallocCountTotal], + thresholds: [ + .peakMemoryResident: .init( + // Tolerate up to 4% of difference compared to the threshold. + relative: [.p90: 4], + // Tolerate up to one million bytes of difference compared to the threshold. + absolute: [.p90: 1_100_000] + ), + .mallocCountTotal: .init( + // Tolerate up to 1% of difference compared to the threshold. + relative: [.p90: 1], + // Tolerate up to 2 malloc calls of difference compared to the threshold. + absolute: [.p90: 2] + ), + ] + ) + + let console = TestConsole() + LoggingSystem.bootstrap( + { label, provider in + ConsoleLogger(label: label, console: console) + }, + metadataProvider: .init { + ["provided1": "from metadata provider", "provided2": "another metadata provider"] + } + ) + + Benchmark("Logging") { benchmark in + var logger = Logger(label: "codes.vapor.console") + logger.logLevel = .trace + logger[metadataKey: "value"] = "one" + + for _ in benchmark.scaledIterations { + logger.info( + "Info", + metadata: ["from-log": "value", "also-from-log": "other"] + ) + } + } +} diff --git a/Benchmarks/ConsoleLoggerBenchmarks/TestConsole.swift b/Benchmarks/ConsoleLoggerBenchmarks/TestConsole.swift new file mode 100644 index 00000000..a7e376fc --- /dev/null +++ b/Benchmarks/ConsoleLoggerBenchmarks/TestConsole.swift @@ -0,0 +1,47 @@ +import ConsoleKit +import Synchronization + +/// A test console that captures input and output for testing purposes. +/// +/// > Warning: This class is a duplicate of the one in the main package, +/// remember to update all when making changes. +final class TestConsole: Console { + let _testInputQueue: Mutex<[String]> = Mutex([]) + + var testInputQueue: [String] { + get { self._testInputQueue.withLock { $0 } } + set { self._testInputQueue.withLock { $0 = newValue } } + } + + let _testOutputQueue: Mutex<[String]> = Mutex([]) + var testOutputQueue: [String] { + get { self._testOutputQueue.withLock { $0 } } + set { self._testOutputQueue.withLock { $0 = newValue } } + } + + let _userInfo: Mutex<[AnySendableHashable: any Sendable]> = Mutex([:]) + var userInfo: [AnySendableHashable: any Sendable] { + get { self._userInfo.withLock { $0 } } + set { self._userInfo.withLock { $0 = newValue } } + } + + init() { + self.testInputQueue = [] + self.testOutputQueue = [] + self.userInfo = [:] + } + + func input(isSecure: Bool) -> String { + return testInputQueue.popLast() ?? "" + } + + func output(_ text: ConsoleText, newLine: Bool) { + testOutputQueue.insert(text.description + (newLine ? "\n" : ""), at: 0) + } + + func report(error: String, newLine: Bool) {} + + func clear(_ type: ConsoleClear) {} + + var size: (width: Int, height: Int) { (width: 32, height: 0) } +} diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift new file mode 100644 index 00000000..75baebba --- /dev/null +++ b/Benchmarks/Package.swift @@ -0,0 +1,46 @@ +// swift-tools-version:6.1 +import PackageDescription + +let package = Package( + name: "benchmarks", + platforms: [ + .macOS(.v15), + .iOS(.v18), + .watchOS(.v11), + .tvOS(.v18), + ], + dependencies: [ + .package(path: "../"), + .package(url: "https://github.com/ordo-one/package-benchmark.git", from: "1.29.2"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.6.3"), + ], + targets: [ + .executableTarget( + name: "ConsoleLoggerBenchmarks", + dependencies: [ + .product(name: "Benchmark", package: "package-benchmark"), + .product(name: "ConsoleLogger", package: "console-kit"), + .product(name: "ConsoleKit", package: "console-kit"), + .product(name: "Logging", package: "swift-log"), + ], + path: "ConsoleLoggerBenchmarks", + swiftSettings: swiftSettings, + plugins: [ + .plugin(name: "BenchmarkPlugin", package: "package-benchmark") + ] + ) + ] +) + +var swiftSettings: [SwiftSetting] { + [ + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("MemberImportVisibility"), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("InternalImportsByDefault"), + .enableUpcomingFeature("MemberImportVisibility"), + .enableUpcomingFeature("InferIsolatedConformances"), + //.enableUpcomingFeature("NonisolatedNonsendingByDefault"), + .enableUpcomingFeature("ImmutableWeakCaptures"), + ] +} diff --git a/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json b/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json new file mode 100644 index 00000000..4bb0ca6a --- /dev/null +++ b/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json @@ -0,0 +1,4 @@ +{ + "mallocCountTotal" : 48, + "peakMemoryResident" : 13311999 +} \ No newline at end of file From 61460219cad6cd7e0958c890781aebf9af9a45dd Mon Sep 17 00:00:00 2001 From: Francesco Paolo Severino Date: Sat, 11 Oct 2025 14:32:59 +0200 Subject: [PATCH 2/4] Address requested changes --- .../ConsoleLoggerBenchmarks.swift | 27 +++++++++++-------- Benchmarks/Package.swift | 6 +---- .../ConsoleLoggerBenchmarks.Logging.p90.json | 2 +- 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift b/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift index 9cc7343f..0fd00f46 100644 --- a/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift +++ b/Benchmarks/ConsoleLoggerBenchmarks/ConsoleLoggerBenchmarks.swift @@ -22,17 +22,22 @@ let benchmarks: @Sendable () -> Void = { ] ) - let console = TestConsole() - LoggingSystem.bootstrap( - { label, provider in - ConsoleLogger(label: label, console: console) - }, - metadataProvider: .init { - ["provided1": "from metadata provider", "provided2": "another metadata provider"] - } - ) - - Benchmark("Logging") { benchmark in + Benchmark( + "Logging", + configuration: .init( + setup: { + let console = TestConsole() + LoggingSystem.bootstrap( + { label, provider in + ConsoleLogger(label: label, console: console) + }, + metadataProvider: .init { + ["provided1": "from metadata provider", "provided2": "another metadata provider"] + } + ) + } + ) + ) { benchmark in var logger = Logger(label: "codes.vapor.console") logger.logLevel = .trace logger[metadataKey: "value"] = "one" diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift index 75baebba..780ed8ce 100644 --- a/Benchmarks/Package.swift +++ b/Benchmarks/Package.swift @@ -4,10 +4,7 @@ import PackageDescription let package = Package( name: "benchmarks", platforms: [ - .macOS(.v15), - .iOS(.v18), - .watchOS(.v11), - .tvOS(.v18), + .macOS(.v15) ], dependencies: [ .package(path: "../"), @@ -40,7 +37,6 @@ var swiftSettings: [SwiftSetting] { .enableUpcomingFeature("InternalImportsByDefault"), .enableUpcomingFeature("MemberImportVisibility"), .enableUpcomingFeature("InferIsolatedConformances"), - //.enableUpcomingFeature("NonisolatedNonsendingByDefault"), .enableUpcomingFeature("ImmutableWeakCaptures"), ] } diff --git a/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json b/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json index 4bb0ca6a..512a674f 100644 --- a/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json +++ b/Benchmarks/Thresholds/ConsoleLoggerBenchmarks.Logging.p90.json @@ -1,4 +1,4 @@ { "mallocCountTotal" : 48, - "peakMemoryResident" : 13311999 + "peakMemoryResident" : 13279231 } \ No newline at end of file From cad7874aa932a37aa03ff7799cab8bcf2e2cd2c2 Mon Sep 17 00:00:00 2001 From: Francesco Paolo Severino Date: Sat, 11 Oct 2025 18:38:05 +0200 Subject: [PATCH 3/4] Remove `.gitignore` symlink --- Benchmarks/.gitignore | 1 - 1 file changed, 1 deletion(-) delete mode 120000 Benchmarks/.gitignore diff --git a/Benchmarks/.gitignore b/Benchmarks/.gitignore deleted file mode 120000 index 5a19b83f..00000000 --- a/Benchmarks/.gitignore +++ /dev/null @@ -1 +0,0 @@ -../.gitignore \ No newline at end of file From 5cdd08065a5ff5f3fa736e4c00dcb69d72c00adc Mon Sep 17 00:00:00 2001 From: Francesco Paolo Severino Date: Sat, 11 Oct 2025 18:40:43 +0200 Subject: [PATCH 4/4] Add `.gitignore` to Benchmarks --- Benchmarks/.gitignore | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 Benchmarks/.gitignore diff --git a/Benchmarks/.gitignore b/Benchmarks/.gitignore new file mode 100644 index 00000000..6adcfa7c --- /dev/null +++ b/Benchmarks/.gitignore @@ -0,0 +1,10 @@ +.build +Packages +*.xcodeproj +Package.pins +Package.resolved +.DS_Store +DerivedData +.swiftpm +Tests/LinuxMain.swift +.vscode