Skip to content

Commit

Permalink
Merge branch 'main' into jw-clientboot-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
weissi authored Jan 3, 2024
2 parents d83cc39 + 5290857 commit e5cef92
Show file tree
Hide file tree
Showing 127 changed files with 10,844 additions and 4,214 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ Package.resolved
DerivedData
.swiftpm
.*.sw?
.vscode/launch.json
9 changes: 9 additions & 0 deletions Benchmarks/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.DS_Store
/.build
/Packages
xcuserdata/
DerivedData/
.swiftpm/configuration/registries.json
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
.netrc
.benchmarkBaselines/
66 changes: 66 additions & 0 deletions Benchmarks/Benchmarks/NIOPosixBenchmarks/Benchmarks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import Benchmark
import NIOPosix

private let eventLoop = MultiThreadedEventLoopGroup.singleton.next()

let benchmarks = {
let defaultMetrics: [BenchmarkMetric] = [
.mallocCountTotal,
]

Benchmark(
"TCPEcho",
configuration: .init(
metrics: defaultMetrics,
timeUnits: .milliseconds,
scalingFactor: .mega
)
) { benchmark in
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)
// In addition this benchmark currently doesn't produce deterministic results on our CI
// and therefore is currently disabled
// 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
}
90 changes: 90 additions & 0 deletions Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEcho.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import NIOCore
import NIOPosix

private final class EchoChannelHandler: ChannelInboundHandler {
fileprivate typealias InboundIn = ByteBuffer

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
context.writeAndFlush(data, promise: nil)
}
}

private final class EchoRequestChannelHandler: ChannelInboundHandler {
fileprivate typealias InboundIn = ByteBuffer

private let messageSize = 10000
private let numberOfWrites: Int
private var batchCount = 0
private let data: NIOAny
private let readsCompletePromise: EventLoopPromise<Void>
private var receivedData = 0

init(numberOfWrites: Int, readsCompletePromise: EventLoopPromise<Void>) {
self.numberOfWrites = numberOfWrites
self.readsCompletePromise = readsCompletePromise
self.data = NIOAny(ByteBuffer(repeating: 0, count: self.messageSize))
}

func channelActive(context: ChannelHandlerContext) {
for _ in 0..<self.numberOfWrites {
context.writeAndFlush(data, promise: nil)
}
}

func channelRead(context: ChannelHandlerContext, data: NIOAny) {
let buffer = self.unwrapInboundIn(data)
self.receivedData += buffer.readableBytes

if self.receivedData == self.numberOfWrites * self.messageSize {
self.readsCompletePromise.succeed()
}
}
}

func runTCPEcho(numberOfWrites: Int, eventLoop: any EventLoop) throws {
let serverChannel = try ServerBootstrap(group: eventLoop)
.childChannelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
try channel.pipeline.syncOperations.addHandler(EchoChannelHandler())
}
}
.bind(
host: "127.0.0.1",
port: 0
).wait()

let readsCompletePromise = eventLoop.makePromise(of: Void.self)
let clientChannel = try ClientBootstrap(group: eventLoop)
.channelInitializer { channel in
channel.eventLoop.makeCompletedFuture {
let echoRequestHandler = EchoRequestChannelHandler(
numberOfWrites: numberOfWrites,
readsCompletePromise: readsCompletePromise
)
try channel.pipeline.syncOperations.addHandler(echoRequestHandler)
}
}
.connect(
host: "127.0.0.1",
port: serverChannel.localAddress!.port!
).wait()

// Waiting for the client to collect all echoed data.
try readsCompletePromise.futureResult.wait()
try serverChannel.close().wait()
try clientChannel.close().wait()
}
95 changes: 95 additions & 0 deletions Benchmarks/Benchmarks/NIOPosixBenchmarks/TCPEchoAsyncChannel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

import NIOCore
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(
wrappingChannelSynchronously: 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(
wrappingChannelSynchronously: channel,
configuration: .init(
inboundType: ByteBuffer.self,
outboundType: ByteBuffer.self
)
)
}
}

let messageSize = 10000

try await withThrowingTaskGroup(of: Void.self) { group in
// This child task is echoing back the data on the server.
group.addTask {
try await serverChannel.executeThenClose { serverChannelInbound in
for try await connectionChannel in serverChannelInbound {
try await connectionChannel.executeThenClose { connectionChannelInbound, connectionChannelOutbound in
for try await inboundData in connectionChannelInbound {
try await connectionChannelOutbound.write(inboundData)
}
}
}
}
}

try await clientChannel.executeThenClose { inbound, outbound in
// This child task is collecting the echoed back responses.
group.addTask {
var receivedData = 0
for try await inboundData in inbound {
receivedData += inboundData.readableBytes

if receivedData == numberOfWrites * messageSize {
return
}
}
}

// Let's start sending data.
let data = ByteBuffer(repeating: 0, count: messageSize)
for _ in 0..<numberOfWrites {
try await outbound.write(data)
}

// Waiting for the child task that collects the responses to finish.
try await group.next()

// Cancelling the server child task.
group.cancelAll()
try await serverChannel.channel.closeFuture.get()
try await clientChannel.channel.closeFuture.get()
}
}
}
33 changes: 33 additions & 0 deletions Benchmarks/Benchmarks/NIOPosixBenchmarks/Util/GlobalExecutor.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#if canImport(Darwin)
import Darwin.C
#elseif canImport(Glibc)
import Glibc
#else
#error("Unsupported platform.")
#endif

// This file allows us to hook the global executor which
// we can use to mimic task executors for now.
typealias EnqueueGlobalHook = @convention(thin) (UnownedJob, @convention(thin) (UnownedJob) -> Void) -> Void

var swiftTaskEnqueueGlobalHook: EnqueueGlobalHook? {
get { _swiftTaskEnqueueGlobalHook.pointee }
set { _swiftTaskEnqueueGlobalHook.pointee = newValue }
}

private let _swiftTaskEnqueueGlobalHook: UnsafeMutablePointer<EnqueueGlobalHook?> =
dlsym(dlopen(nil, RTLD_LAZY), "swift_task_enqueueGlobal_hook").assumingMemoryBound(to: EnqueueGlobalHook?.self)
41 changes: 41 additions & 0 deletions Benchmarks/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// swift-tools-version: 5.7
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftCertificates open source project
//
// Copyright (c) 2023 Apple Inc. and the SwiftCertificates project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftCertificates project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import PackageDescription

let package = Package(
name: "benchmarks",
platforms: [
.macOS("14"),
],
dependencies: [
.package(path: "../"),
.package(url: "https://github.com/ordo-one/package-benchmark.git", from: "1.11.1"),
],
targets: [
.executableTarget(
name: "NIOPosixBenchmarks",
dependencies: [
.product(name: "Benchmark", package: "package-benchmark"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOPosix", package: "swift-nio"),
],
path: "Benchmarks/NIOPosixBenchmarks",
plugins: [
.plugin(name: "BenchmarkPlugin", package: "package-benchmark")
]
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mallocCountTotal" : 108
}
3 changes: 3 additions & 0 deletions Benchmarks/Thresholds/5.7/NIOPosixBenchmarks.TCPEcho.p90.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mallocCountTotal" : 110
}
3 changes: 3 additions & 0 deletions Benchmarks/Thresholds/5.8/NIOPosixBenchmarks.TCPEcho.p90.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mallocCountTotal" : 110
}
3 changes: 3 additions & 0 deletions Benchmarks/Thresholds/5.9/NIOPosixBenchmarks.TCPEcho.p90.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mallocCountTotal" : 110
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"mallocCountTotal" : 108
}
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ For this reason, whenever you add new tests **you have to run a script** that ge

### Make sure your patch works for all supported versions of swift

The CI will do this for you. You can use the docker-compose files included if you wish to check locally. Currently all versions of swift >= 5.6 are supported. For example usage of docker compose see the main [README](./README.md#an-alternative-using-docker-compose)
The CI will do this for you. You can use the docker-compose files included if you wish to check locally. Currently all versions of swift >= 5.7 are supported. For example usage of docker compose see the main [README](./README.md#an-alternative-using-docker-compose)

### Make sure your code is performant

Expand Down
7 changes: 5 additions & 2 deletions IntegrationTests/tests_02_syscall_wrappers/defines.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function make_package() {
fi

cat > "$tmpdir/syscallwrapper/Package.swift" <<"EOF"
// swift-tools-version:5.6
// swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
Expand All @@ -36,7 +36,10 @@ let package = Package(
dependencies: ["CNIOLinux", "CNIODarwin", "NIOCore"]),
.target(
name: "CNIOLinux",
dependencies: []),
dependencies: [],
cSettings: [
.define("_GNU_SOURCE")
]),
.target(
name: "CNIODarwin",
dependencies: []),
Expand Down
Loading

0 comments on commit e5cef92

Please sign in to comment.