Files
swift-mirror/benchmark/single-source/ExecutorEnqueue.swift
Konrad `ktoso` Malawski 900f6906cd [Concurrency] make enqueue forwarding chain inlinable (#87611)
The inlinable certainly wont hurt on these "forwarding" methods, though
I was not able to confirm a noticable difference at least using this
benchmark.

Since adding those is definitely good anyway, I didn't dig much further,
let's just add them.

Assisted by: claude to make the benchmark, manually verified all changes
ran benchmarks and checked results etc
2026-03-02 08:35:20 -08:00

106 lines
3.1 KiB
Swift

//===--- ExecutorEnqueue.swift --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2024 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
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// Compares actor hop overhead when a SerialExecutor implements the legacy
// enqueue(_ job: UnownedJob) overload versus the preferred
// enqueue(_ job: consuming ExecutorJob) overload (SE-0392, Swift 5.9).
//
//===----------------------------------------------------------------------===//
import TestsUtils
public var benchmarks: [BenchmarkInfo] {
guard #available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *) else {
return []
}
return [
BenchmarkInfo(
name: "ExecutorEnqueue.UnownedJob",
runFunction: run_UnownedJobActor,
tags: [.concurrency]
),
BenchmarkInfo(
name: "ExecutorEnqueue.ExecutorJob",
runFunction: run_ExecutorJobActor,
tags: [.concurrency]
),
]
}
// MARK: - Executors
/// Implements the legacy enqueue(_ job: UnownedJob) overload.
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private final class InlineUnownedJobExecutor: SerialExecutor, @unchecked Sendable {
// Implementing the deprecated enqueue(UnownedJob) overload is intentional;
// the benchmark measures the overhead of this delegation path.
func enqueue(_ job: UnownedJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
}
/// Implements the preferred enqueue(_ job: consuming ExecutorJob) overload.
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private final class InlineExecutorJobExecutor: SerialExecutor, @unchecked Sendable {
func enqueue(_ job: consuming ExecutorJob) {
job.runSynchronously(on: self.asUnownedSerialExecutor())
}
}
// MARK: - Actors
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private actor UnownedJobActor {
private var counter: Int = 0
private let _executor = InlineUnownedJobExecutor()
nonisolated var unownedExecutor: UnownedSerialExecutor {
_executor.asUnownedSerialExecutor()
}
func increment() {
counter &+= 1
}
}
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private actor ExecutorJobActor {
private var counter: Int = 0
private let _executor = InlineExecutorJobExecutor()
nonisolated var unownedExecutor: UnownedSerialExecutor {
_executor.asUnownedSerialExecutor()
}
func increment() {
counter &+= 1
}
}
// MARK: - Benchmark functions
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private func run_UnownedJobActor(_ n: Int) async {
let a = UnownedJobActor()
for _ in 0 ..< n {
await a.increment()
}
}
@available(macOS 14.0, iOS 17.0, tvOS 17.0, watchOS 10.0, *)
private func run_ExecutorJobActor(_ n: Int) async {
let a = ExecutorJobActor()
for _ in 0 ..< n {
await a.increment()
}
}