文章摘要
这篇文章介绍了SBCL Fibers,一种轻量级用户态协作线程的实现方案。文章描述了其设计目标、编程API以及当前开发进展,该项目仍在积极开发中,代码可在GitHub的fibers-v2分支上试用。
文章总结
SBCL Fibers:轻量级协作式线程
本文介绍了SBCL(Steel Bank Common Lisp)中轻量级用户态协作式线程(Fibers)的实现方案,该实现正在积极开发中,细节可能发生变化。
1. 引言
1.1 动机:为什么需要Fibers?
许多服务器工作负载是并发而非并行的。一个处理10,000个连接的Web服务器大部分时间都在等待网络I/O,每个请求的实际计算量很小。自然的编程模型是为每个连接分配一个控制线程,但OS线程在这种规模下开销过大。
Fibers提供了第三种选择:Fibers是拥有独立控制栈和绑定栈的用户空间线程,由库级调度器而非内核调度。Fibers保持了顺序编程模型,同时实现了事件驱动I/O的资源效率。
1.2 设计目标
实现遵循以下优先级: 1. GC下的正确性 2. 透明集成 3. 低切换开销 4. 支持大量纤程 5. 多核利用
1. 3 术语
- Fiber:轻量级协作线程
- Carrier thread:运行Fibers的OS线程
- Scheduler:每个carrier的管理结构
- Yield:Fiber主动暂停自己
- Resume:调度器恢复Fiber执行
2. 编程API
2.1 创建和运行Fibers
使用make-fiber创建Fiber:
lisp
(make-fiber function &key name stack-size binding-stack-size initial-bindings)
2.2 让出和等待
fiber-yield暂停当前Fiber:
lisp
(fiber-yield &optional wake-condition)
2.3 Fiber休眠和定时等待
fiber-sleep暂停当前Fiber指定时间:
lisp
(fiber-sleep seconds)
2.4 Fiber暂停(基于条件的暂停/恢复)
fiber-park是通用暂停原语:
lisp
(fiber-park predicate &key timeout)
2.5 Fiber连接
fiber-join等待目标Fiber完成:
lisp
(fiber-join target &key timeout)
3. 架构概述
3.1 Carrier线程和调度器
运行时围绕两级层次结构组织: - Carrier线程是普通SBCL OS线程 - 每个调度器组包含多个调度器
3.2 Fiber生命周期状态机
Fiber经历五个状态:
:created → :runnable → :running
↑ |
| yield/wake
+── :suspended
|
v
:dead
4. 上下文切换
4.1 寄存器保存/恢复约定
仅保存平台ABI定义的被调用者保存寄存器。
4.2 fiber_switch汇编例程
上下文切换的核心,作为Lisp汇编例程实现。
5. 栈管理
5.1 控制栈布局和保护页
每个Fiber的控制栈是连续区域,底部有保护页。
5.2 绑定栈(独立分配)
每个Fiber有独立的绑定栈用于动态变量绑定。
6. 动态变量绑定(TLS)
6.1 问题:unbind_to_here清零条目
解决方案是使用TLS覆盖方法。
7. 垃圾收集器集成
7.1 双列表设计
all_fiber_gc_info:已创建但未销毁的Fiber列表all_active_fiber_contexts:当前运行调度器的carrier列表
8. 调度器设计
8.1 调度器循环
核心结构:
fallback
init-carrier-fiber-context()
loop {
fiber = fast-path-pop() or (maintenance + pop-or-steal)
...
}
9. 工作窃取
9.1 Chase-Lev无锁双端队列
每个调度器的运行队列是Chase-Lev工作窃取队列。
10. I/O多路复用
10.1 平台抽象
使用平台特定的I/O多路复用: - Linux:epoll - BSD/macOS:kqueue - 回退:批量poll()
11. 截止时间调度
11.1 带内联索引的二叉最小堆
调度器维护按fiber-deadline排序的二叉最小堆。
12. Fiber终止和清理
12.1 Lisp跳板(fiber-trampoline)
当Fiber首次启动时的入口路径。
13. 与SBCL的集成
13.1 线程结构扩展
SBCL线程结构扩展了三个Fiber相关槽。
14. 性能
14.1 HTTP基准测试
在10,000连接时,Fibers保持102,710 r/s,而线程降至55,493 r/s。
15. 平台支持
支持x86-64、ARM64、ARM32、PPC64、PPC32和RISC-V平台。
附录A:在Hunchentoot中使用Fibers
Hunchentoot的标准任务管理器创建OS线程,而Fiber任务管理器用Fibers替换这些线程。
A.1 Fiber任务管理器
lisp
(defclass fiber-taskmaster (hunchentoot:taskmaster)
((group :accessor fiber-taskmaster-group
:initform nil)
(carrier-count :initarg :carrier-count
:initform nil
:accessor fiber-taskmaster-carrier-count)))
A.2 任务管理器方法
包括execute-acceptor、handle-incoming-connection和shutdown方法。
A.3 启动服务器
示例展示了如何用Fiber任务管理器启动Hunchentoot服务器。
A.4 工作原理
详细说明了Fiber任务管理器的工作流程。
A.5 SSL
讨论了使用SSL时的注意事项和纯TLS实现的优势。
A.6 注意事项
包括会话锁定、超时、日志记录和最大连接数限制等考虑因素。
评论总结
以下是评论内容的总结:
关于命名争议
- 有用户调侃应命名为"Anthony Green Threads"(评论1:"They should be called Anthony Green Threads")
- 另一用户偏好"fiber"而非"green threads",但承认后者更常见(评论2:"I personally like the name fiber better than green threads")
技术文档需求
- 用户询问内存区域(memory arena)功能的相关文档(评论3:"Is there a similar document for the memory arena feature?")
关于SBCL的讨论
- 有用户简单提及SBCL(Steel Bank Common Lisp)(评论4:"SBCL - Steel Bank Common Lisp")
- 另一用户建议查看邮件列表以获取背景信息(评论5:"I strongly recommend having a look at the mailing list")
关于LLM能力的质疑
- 用户质疑LLM平衡括号的能力(评论6:"I thought LLMs were bad at balancing parentheses?")
Actor模型与性能讨论
- 用户批评Fiber的256KB栈开销过大,推崇Actor模型(评论7:"256Kb stack per Fiber is still insane overhead compared to Actors")
- 指出多数开发者不了解Actor模型的性能优势(评论7:"less than 2% of devs even know what the Actor model is")
标题误解
- 用户误以为这是材料科学论文(评论8:"I really thought this was gonna be a sick material science paper")