From 0cb17ae37258218668598973002c9d01b07a04ba Mon Sep 17 00:00:00 2001 From: Franz Busch Date: Sun, 8 Oct 2023 21:49:57 +0100 Subject: [PATCH] Add `NIOAsyncChannel` benchmark # Motivation We want to benchmark our `NIOAsyncChannel` to see how it compares to the synchronous implementation with `ChannelHandler`s # Modification This PR adds a new `TCPEchoAsyncChannel` benchmark that mimics the `TCPEcho` benchmark but uses our new async bridges. Since Swift Concurrency, is normally using a global executor this benchmark would have quite high variation. To reduce this variant I introduced code to hook the global executor and set an `EventLoop` as the executor. In the future, if we get task executors we can change the code to us them instead. # Result New baseline benchmarks for the `NIOAsyncChannel`. --- .../NIOPosixBenchmarks/Benchmarks.swift | 34 ++++++- .../NIOPosixBenchmarks/TCPEcho.swift | 3 +- .../TCPEchoAsyncChannel.swift | 89 +++++++++++++++++++ .../Util/GlobalExecutor.swift | 33 +++++++ Benchmarks/Package.swift | 2 +- .../5.10/NIOPosixBenchmarks.TCPEcho.p90.json | 2 +- ...sixBenchmarks.TCPEchoAsyncChannel.p90.json | 3 + .../5.7/NIOPosixBenchmarks.TCPEcho.p90.json | 2 +- .../5.8/NIOPosixBenchmarks.TCPEcho.p90.json | 2 +- .../5.9/NIOPosixBenchmarks.TCPEcho.p90.json | 2 +- ...sixBenchmarks.TCPEchoAsyncChannel.p90.json | 3 + .../main/NIOPosixBenchmarks.TCPEcho.p90.json | 2 +- ...sixBenchmarks.TCPEchoAsyncChannel.p90.json | 3 + 13 files changed, 171 insertions(+), 9 deletions(-) create mode 100644 Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEchoAsyncChannel.swift create mode 100644 Benchmarks/Benchmarks/NIOPosixBenchmarks/Util/GlobalExecutor.swift create mode 100644 Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json create mode 100644 Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json create mode 100644 Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift index f579891e75..1590fcd65a 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift @@ -13,6 +13,9 @@ //===----------------------------------------------------------------------===// import Benchmark +import NIOPosix + +private let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next() let benchmarks = { let defaultMetrics: [BenchmarkMetric] = [ @@ -27,6 +30,35 @@ let benchmarks = { scalingFactor: .mega ) ) { benchmark in - try runTCPEcho(numberOfWrites: benchmark.scaledIterations.upperBound) + try runTCPEcho( + numberOfWrites: benchmark.scaledIterations.upperBound, + eventLoop: eventLoop + ) + } + + // This benchmark is only available above 5.9 since our EL conformance + // to serial executor is also gated behind 5.9. + #if compiler(>=5.9) + Benchmark( + "TCPEchoAsyncChannel", + configuration: .init( + metrics: defaultMetrics, + timeUnits: .milliseconds, + scalingFactor: .mega, + setup: { + swiftTaskEnqueueGlobalHook = { job, _ in + eventLoop.executor.enqueue(job) + } + }, + teardown: { + swiftTaskEnqueueGlobalHook = nil + } + ) + ) { benchmark in + try await runTCPEchoAsyncChannel( + numberOfWrites: benchmark.scaledIterations.upperBound, + eventLoop: eventLoop + ) } + #endif } diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEcho.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEcho.swift index 1c656ea217..b7124ad50d 100644 --- a/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEcho.swift +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEcho.swift @@ -55,8 +55,7 @@ private final class EchoRequestChannelHandler: ChannelInboundHandler { } } -func runTCPEcho(numberOfWrites: Int) throws { - let eventLoop = MultiThreadedEventLoopGroup.singleton.next() +func runTCPEcho(numberOfWrites: Int, eventLoop: any EventLoop) throws { let serverChannel = try ServerBootstrap(group: eventLoop) .childChannelInitializer { channel in channel.eventLoop.makeCompletedFuture { diff --git a/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEchoAsyncChannel.swift b/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEchoAsyncChannel.swift new file mode 100644 index 0000000000..88abcf5470 --- /dev/null +++ b/Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEchoAsyncChannel.swift @@ -0,0 +1,89 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftNIO open source project +// +// Copyright (c) 2023 Apple Inc. and the SwiftNIO project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftNIO project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + +@_spi(AsyncChannel) import NIOCore +@_spi(AsyncChannel) import NIOPosix + +func runTCPEchoAsyncChannel(numberOfWrites: Int, eventLoop: EventLoop) async throws { + let serverChannel = try await ServerBootstrap(group: eventLoop) + .bind( + host: "127.0.0.1", + port: 0 + ) { channel in + channel.eventLoop.makeCompletedFuture { + return try NIOAsyncChannel( + synchronouslyWrapping: channel, + configuration: .init( + inboundType: ByteBuffer.self, + outboundType: ByteBuffer.self + ) + ) + } + } + + let clientChannel = try await ClientBootstrap(group: eventLoop) + .connect( + host: "127.0.0.1", + port: serverChannel.channel.localAddress!.port! + ) { channel in + channel.eventLoop.makeCompletedFuture { + return try NIOAsyncChannel( + synchronouslyWrapping: channel, + configuration: .init( + inboundType: ByteBuffer.self, + outboundType: ByteBuffer.self + ) + ) + } + } + + let bufferSize = 10000 + + try await withThrowingTaskGroup(of: Void.self) { group in + // This child task is echoing back the data on the server. + group.addTask { + for try await connectionChannel in serverChannel.inboundStream { + for try await inboundData in connectionChannel.inboundStream { + try await connectionChannel.outboundWriter.write(inboundData) + } + } + } + + // This child task is collecting the echoed back responses. + group.addTask { + var receivedData = 0 + for try await inboundData in clientChannel.inboundStream { + receivedData += inboundData.readableBytes + + if receivedData == numberOfWrites * bufferSize { + return + } + } + } + + // Let's start sending data. + let data = ByteBuffer(repeating: 0, count: bufferSize) + for _ in 0.. Void) -> Void + +var swiftTaskEnqueueGlobalHook: EnqueueGlobalHook? { + get { _swiftTaskEnqueueGlobalHook.pointee } + set { _swiftTaskEnqueueGlobalHook.pointee = newValue } +} + +private let _swiftTaskEnqueueGlobalHook: UnsafeMutablePointer = + dlsym(dlopen(nil, RTLD_LAZY), "swift_task_enqueueGlobal_hook").assumingMemoryBound(to: EnqueueGlobalHook?.self) diff --git a/Benchmarks/Package.swift b/Benchmarks/Package.swift index cb7ef412e1..8797a9249f 100644 --- a/Benchmarks/Package.swift +++ b/Benchmarks/Package.swift @@ -18,7 +18,7 @@ import PackageDescription let package = Package( name: "benchmarks", platforms: [ - .macOS(.v13), + .macOS("14"), ], dependencies: [ .package(path: "../"), diff --git a/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEcho.p90.json index a920579610..fa70aea890 100644 --- a/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 93 + "mallocCountTotal" : 90 } \ No newline at end of file diff --git a/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json b/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json new file mode 100644 index 0000000000..ddd4e94cf2 --- /dev/null +++ b/Benchmarks/Thresholds/5.10/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json @@ -0,0 +1,3 @@ +{ + "mallocCountTotal" : 5554895 +} \ No newline at end of file diff --git a/Benchmarks/Thresholds/5.7/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/5.7/NIOPosixBenchmarks.TCPEcho.p90.json index 18de727583..1859f424c5 100644 --- a/Benchmarks/Thresholds/5.7/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/5.7/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 95 + "mallocCountTotal" : 92 } \ No newline at end of file diff --git a/Benchmarks/Thresholds/5.8/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/5.8/NIOPosixBenchmarks.TCPEcho.p90.json index 18de727583..1859f424c5 100644 --- a/Benchmarks/Thresholds/5.8/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/5.8/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 95 + "mallocCountTotal" : 92 } \ No newline at end of file diff --git a/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEcho.p90.json index 18de727583..1859f424c5 100644 --- a/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 95 + "mallocCountTotal" : 92 } \ No newline at end of file diff --git a/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json b/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json new file mode 100644 index 0000000000..ffdf0ae74c --- /dev/null +++ b/Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json @@ -0,0 +1,3 @@ +{ + "mallocCountTotal" : 5636901 +} \ No newline at end of file diff --git a/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEcho.p90.json b/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEcho.p90.json index a920579610..fa70aea890 100644 --- a/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEcho.p90.json +++ b/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEcho.p90.json @@ -1,3 +1,3 @@ { - "mallocCountTotal" : 93 + "mallocCountTotal" : 90 } \ No newline at end of file diff --git a/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json b/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json new file mode 100644 index 0000000000..ddd4e94cf2 --- /dev/null +++ b/Benchmarks/Thresholds/main/NIOPosixBenchmarks.TCPEchoAsyncChannel.p90.json @@ -0,0 +1,3 @@ +{ + "mallocCountTotal" : 5554895 +} \ No newline at end of file