所有的 Coroutine ,无论有栈无栈还是夹在中间的什么混合模式,这个代码总是要被执行的。谁来执行呢?我援引 Solaris 最经典的 LWP 设计:
本质来说,从一个纯粹的 User program 或者被语言包裹好的这个"乌托邦"来看,OS Thread 就是新的 CPU Cores ,我们不妨叫他 Coroutine Process Unit ,其他的概念几乎等效,无非就是在这个新的 CPU Cores 上看这个新的调度器在做什么操作罢了;只不过我们有一个得天独厚的优势,就是这一切都发生在 User Space ,所有的 context switch 之类的操作都有很大空间不会切换到 Kernel Space ,也不需要跟特定 OS 信息交互,这样就能节省一大部分开销。通常认为的所谓
但是这能说明谁比谁更快吗?尝试用你的 workload 回答我下面三个问题:
1. 这个程序对 CPU 的 locality 要求有多高?无论是基于 NUMA 的同 Node 访问或者 Cache line 的访问优化,你需要它表现到什么样子?
2. 这个程序对 IO 的定制需求如何?一个 epoll/kqueue 就足够,还是需要 DMA/RDMA 这种需要外部 driver 交互的 IO 支持? Runtime 跟你的通信调度如何处理?
3. 这个程序的计算密集程度如何?是可以切成无互访的无状态并发,直接分发完数据 CPU 猛算,还是有高频的互访,实际上存在特定的通信效率瓶颈?
这三个问题只是一个例子,如何去考虑线程跟 Coroutine 的开销。大部分 Coroutine 退化到最后就是一堆普通的线程,插入了一些语言 Runtime 带来的额外钩子,这些钩子的成本显然随着 Coroutine 的增加会有所上升。
但是这个边际成本出现剧增的点在哪儿?不同 Workload 的答案不一样,拍脑袋得不出答案。不要想当然认为线程跟 coroutine 谁更好谁更快,做工程的要拿 benchmark 说话,而不是讨论假大空的概念。如果真对这个问题感兴趣,可以在一些 HPC 任务上跑跑 poc ,看看 HPC 工程师调 CPU Affinity 和 openmp 的观察和优化逻辑。