ITADN
swift-foundations/swift-io · 文件 下载 ZIP
文件最后提交记录最后更新时间
README.md
以下内容由 AI 翻译,如有问题请点此提交 issue 反馈

swift-io

CI Development Status

一个用于 Swift 的高性能异步 I/O 执行器。通过专用工作线程、有界队列和确定性的关闭语义,将阻塞系统调用与 Swift 的协作式线程池隔离开来。

主要特性

  • 专用线程池 - 阻塞 I/O 永远不会耗尽 Swift 的协作式执行器
  • 基于上下文的完成 - 消除字典查找;任务携带其自身的 continuation
  • 基于状态转换的信号 - 仅在空→非空时发出信号;每次唤醒时排空批次
  • 仅移动资源 - 针对 ~Copyable 泛型化,具有类型安全的槽位传输
  • 端到端类型化抛出 - API 表面没有 any Error
  • Swift 6 严格并发 - 完全符合 Sendable 规范,零数据竞争

设计哲学

swift-io 专为基础设施代码设计,在这些场景中,正确性、确定性和资源边界比绝对峰值吞吐量更重要。它有意以无界排队换取负载下的可预测行为。

非目标: swift-io 不旨在通过无界排队来最大化吞吐量。

性能

比较 swift-io 与 SwiftNIO 的 NIOThreadPool 的基准测试(release 模式,arm64,Apple M1)。报告了中位数;在尾部延迟差异显著时显示 p95/p99。

基准测试模拟短阻塞工作负载(每个 10µs)。实际 I/O 由系统调用主导,从而减少了相对开销差异。这些基准测试隔离了执行器行为;它们不模拟端到端 I/O 延迟。

吞吐量

基准测试swift-ioNIOThreadPool差异
顺序执行 (1000 × 10µs 操作)4.51ms7.15ms快 37%
并发执行 (1000 × 10µs 操作)1.72ms1.43msNIO 快 17%

开销 (每操作,中位数)

基准测试swift-ioNIOThreadPool差异
线程调度4.00µs7.88µs快 49%
成功路径3.96µs7.83µs快 49%
失败路径4.46µs10.79µs快 59%
队列准入4.13µs7.83µs快 47%

竞争

场景swift-ioswift-io shardedNIOThreadPool备注
中等 (10:1)216µs224µs182µsNIO 中位数胜出;p95 swift-io 253µs 对比 NIO 671µs
高 (100:1)1.01ms568µs632µsSharded 比 NIO 快 10%
极高 (1000:1)3.45ms2.54ms2.55msSharded 与 NIO 持平

设计优势

机制收益测量结果
基于上下文的完成消除共享字典查找83ns 对比 1.50µs (18×)
分片通道降低负载下的锁竞争在 100:1 下比分片前快 40%

总结

swift-io 优先考虑可预测的延迟、有界的资源使用和确定性的关闭,而非峰值吞吐量。在具有无界队列的高并发工作负载下,NIOThreadPool 仍具有优势;swift-io 在负载下表现出更低的每操作开销和更稳定的尾部行为。

何时使用 swift-io 而非 NIOThreadPool

  • 使用 swift-io 当你需要有界队列、确定性关闭、类型化错误保留或可预测的尾部延迟时。
  • 使用 NIOThreadPool 当你希望获得最大并发吞吐量并接受无界队列语义时。

为什么选择 swift-io?

从概念上讲,swift-io 是一个由 actor 管理的独占资源池,运行在有界的阻塞通道上。

Swift 的协作线程池专为快速、非阻塞的工作而设计。当你混入阻塞系统调用时:

问题协作线程池swift-io
阻塞系统调用使协作线程饥饿专用线程隔离阻塞工作
等待者管理手动处理 continuation具有背压的有界 FIFO 队列
资源清理手动,易出错确定性拆除策略
取消语义不一致定义明确:接受前/接受后
仅移动资源无原生支持基于 ~Copyable 的泛型,采用槽位模式
错误处理无类型 throws带有 IO.Lifecycle.Error<IO.Error<E>> 的类型化 throws

安装

dependencies: [
    .package(url: "https://github.com/swift-foundations/swift-io.git", from: "0.2.0")
]
.target(
    name: "YourTarget",
    dependencies: [
        .product(name: "IO", package: "swift-io"),
    ]
)

要求:

  • Swift 6.3.1+ (swift-tools-version: 6.3.1)
  • Apple 平台:macOS 26 / iOS 26 / tvOS 26 / watchOS 26
  • Linux 和 Windows:参见 平台支持

快速开始

一次性阻塞操作

最简单的模式——在专用线程上运行阻塞操作:

import IO

let pool = IO.Executor.Pool<Void>()

let data = try await pool.run {
    try blockingSyscall()  // Runs on dedicated thread, not cooperative pool
}

await pool.shutdown()

托管资源

对于长生命周期资源(文件句柄、连接),请注册它们并使用事务:

import IO

let pool = IO.Executor.Pool<FileHandle>()

// Register → get ID
let id = try await pool.register {
    try FileHandle.open(path)
}

// Transaction → exclusive access
let data = try await pool.transaction(id) { handle in
    try handle.read()
}

// Destroy → cleanup
try pool.destroy(id)
await pool.shutdown()

领域门面模式

在生产环境中,将池封装在特定领域的 API 中(参见 swift-file-system):

public actor FileSystem {
    private let pool: IO.Executor.Pool<FileHandle>

    public init() { self.pool = IO.Executor.Pool() }

    public func read(
        at path: String
    ) async throws(IO.Lifecycle.Error<IO.Error<ReadError>>) -> Data {
        try await pool.run {
            try Data(contentsOfFile: path)
        }
    }

    public func shutdown() async { await pool.shutdown() }
}

错误处理

swift-io 使用类型化抛出。池方法抛出 IO.Lifecycle.Error<IO.Error<E>>,您可以对其进行穷举模式匹配:

do {
    let value = try await pool.run {
        try myOperation()  // throws MyError
    }
} catch {
    switch error {
    case .shutdownInProgress:
        // Pool is shutting down
    case .cancellation:
        // Task was cancelled
    case .failure(let ioError):
        switch ioError {
        case .leaf(let myError):
            // myError is MyError (typed!)
        case .handle(let handleError):
            // e.g. .notFound, .scopeMismatch
        case .executor(let execError):
            // e.g. .waiterQueueFull
        case .lane(let laneError):
            // Lane infrastructure error
        }
    }
}

错误模型

swift-io 端到端使用类型化抛出。公共 API 不会抛出 any Error。操作错误被保留为 E 并提升为 IO.Lifecycle.Error<IO.Error<E>>

错误层次结构:

IO.Lifecycle.Error<E>
├── .shutdownInProgress    // Lifecycle: pool shutting down
├── .cancelled             // Lifecycle: task cancelled
└── .failure(E)            // Wraps operational errors
    └── IO.Error<Leaf>
        ├── .leaf(Leaf)    // Your operation's error type
        ├── .handle(...)   // Handle errors (.notFound, .scopeMismatch)
        ├── .executor(...) // Executor errors (.waiterQueueFull)
        └── .lane(...)     // Lane errors (.queue(.full), .overloaded)

架构

┌─────────────────────────────────────────────┐
│                    IO                        │  ← Pool, Handle.ID, Error
├─────────────────────────────────────────────┤
│              IO Blocking                     │  ← Lane abstraction
├─────────────────────────────────────────────┤
│           IO Blocking Threads                │  ← Thread pool + signal optimization
├─────────────────────────────────────────────┤
│              IO Primitives                   │  ← Core types, platform abstraction
└─────────────────────────────────────────────┘

主要类型

类型用途
IO.Executor.Pool<Resource>基于 Actor 的资源池,支持事务访问
IO.Handle.ID已注册资源的范围标识符
IO.Blocking.Lane执行后端(.threads().sharded()
IO.Lifecycle.Error<E>生命周期封装(关闭、取消)
IO.Error<E>保留操作错误的类型化错误

执行模型

Swift Task                    Lane (Thread Pool)
    │                              │
    ├─── run(operation) ──────────►│
    │    (suspends)                │
    │                              ├─── execute on worker thread
    │                              │
    │◄── resume with result ───────┤
    │    (context-based, no lookup)│

设计细节

信号优化

工作线程使用基于状态转换的信号机制以最小化内核开销:

  • 休眠者跟踪 - 仅当工作线程实际处于等待状态时才发送信号
  • 空→非空转换 - 每个批次仅发送一次信号,而非每个任务
  • 排空循环 - 每个唤醒周期最多处理 16 个任务

与按任务发送信号相比,这消除了约 90% 的虚假 pthread_cond_signal 调用。

基于上下文的完成

任务携带其完成上下文,从而消除了共享字典状态和持锁哈希操作:

// Traditional: O(1) amortized but with hash overhead + lock contention
completions[ticket] = result  // store under lock
let result = completions.removeValue(forKey: ticket)  // lookup under lock

// swift-io: Direct pointer, zero lookup, no shared dictionary
do { try job.context.complete(with: result) } catch {}  // 83ns, atomic CAS

保证

swift-io 保证的内容:

  • 恰好一次的 continuation 恢复
  • 通过容量受限的队列实现有界内存
  • 带有在途完成的确定性关闭
  • 取消安全性

公平性:

  • 队列顺序为 FIFO
  • 在竞争条件下调度为尽力而为
  • 不保证完成顺序(drain 循环可能会重新排序)

swift-io 不保证的内容:

  • 接受后的系统调用中断
  • 高竞争条件下的严格 FIFO 完成
  • 跨进程协调

配置

// Custom thread pool
let pool = IO.Executor.Pool<MyResource>(
    lane: .threads(.init(count: 4, queueLimit: 128)),
    handleWaitersLimit: 32
)

// Sharded lane for reduced contention
let pool = IO.Executor.Pool<MyResource>(
    lane: .sharded(count: 4)
)

// Custom teardown
let pool = IO.Executor.Pool<FileHandle>(
    teardown: .run { handle in
        try? handle.close()
    }
)

平台支持

CI 目标为 macOS、Linux 和 Windows;请参阅 workflow runs 以了解当前状态。

平台CI 目标备注
macOSSwift 6.3.1, debug在 CI 矩阵中指定
Linux (Ubuntu)Swift 6.3.1, release在 CI 矩阵中指定
WindowsSwift 6.3.1在 CI 矩阵中指定
iOS/tvOS/watchOS支持(与 macOS 使用相同的代码库)

相关包

状态与维护者

此包为公开 Alpha 版(1.0 之前):接口正在稳定中,API 可能会在次要版本之间发生变化。

Coen ten Thije Boonkkamp 维护 — 提供 Swift 基础设施和文档系统咨询服务:coen@coenttb.com

许可证

Apache 2.0 - 详见 LICENSE