首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >DeepSeek 3FS源码分析(2) 协程如何突破单线程限制,实现多核并发

DeepSeek 3FS源码分析(2) 协程如何突破单线程限制,实现多核并发

作者头像
早起的鸟儿有虫吃
发布2025-12-24 18:11:35
发布2025-12-24 18:11:35
1420
举报

行业应用对比:从电信到文件系统

你有没有发现,在传统的电信行业,分布式数据库很少使用协程(Coroutine),而到了文件系统领域,3FS (Fire-Flyer File System) 直接采用 Facebook 开源的 Folly 提供协程。

画外音:

此刻这个不是主要解决的,我是使用者,我最关系的协程帮助我解决什么问题

Folly 协程框架深度解析

设计理念

Folly 是面向多核架构和硬件特性进行设计的。Folly 的强大之处在于它将协程的轻量级并发与线程池的并行性结合了起来。

核心技术特性

单线程内的并发

一个线程通过协程可以高效处理数万甚至数十万个并发 I/O 操作

通过 folly::coro::Task 配合异步 I/O 原语(如 io_uring)实现

跨线程的并行

通过 folly::IOThreadPoolExecutor 等线程池分发任务

将大量协程任务分发到多个线程上并行执行

直接利用多核 CPU 的计算能力,提升系统整体吞吐量

新一代系统架构演进:Ceph 案例

Seastar 异步框架重构

新一代 Ceph 直接利用异步框架 Seastar 重构 OSD。

Seastar 核心特性

高性能:借助于无锁编程和精心设计的内存管理,能在多核 CPU 上实现接近硬件极限的性能

异步非阻塞:使用未来(futures)简化复杂异步逻辑的编码过程

自含 TCP/IP 栈:内置的高性能网络栈绕过操作系统瓶颈

基础概念深度理解

并发与并行的本质区别

多线程场景分析: 在传统电信行业,一台 100G/64 核服务器上,任务有积压,通过多线程方式提高并发,这个并发本质上是依赖多核并行计算。

核心定义对比

概念

定义

关键特征

并发

逻辑上的同时发生

系统具有处理多个任务的能力,但不一定在同一时刻执行

并行

物理上的同时执行

必须在同一时刻有多个任务正在被执行

技术洞察

计算是并行执行,但访问共享资源需要串行执行

从互斥锁演进到无锁编程

epoll_wait 使单个线程能高效管理数万并发连接

事件驱动方式让 CPU 仅在任务就绪时处理,避免资源浪费

协程的本质价值

重要结论

协程本身不提供并行能力

一个线程内的所有协程,在任何时刻只有一个在运行

协程是并发而非并行的

核心价值:解决高并发问题,特别在 I/O 密集型场景

协程库的技术演进

传统协程解决方案的局限性

核心问题:协程库如何提高并发能力?

传统答案的问题

协程库专注解决单个线程内用户态调用问题

并发问题通过开启多个进程解决

但进程消耗内存过大(如 10GB/进程)

这种方式不通用,未大规模推广

架构演进历史

1

多进程(MP)时代

代表:Apache web server

问题:创建进程开销过高

调度单位:进程

2

多线程(MT)时代

改进:创建线程服务新用户

问题:线程上下文切换、锁竞争

调度单位:线程

3

事件驱动状态机(EDSM)

基于 I/O 复用机制处理大量并发

问题:程序一体化,开发复杂

协程的技术突破

核心优势

调度单位减小到函数级别

上下文切换不需要内核参与

无系统调用、无锁竞争、无信号处理

保持线性处理逻辑,提高开发效率

协程实现技术详解

协程的魔法原理

代码语言:javascript
复制
// 伪代码示意
coroutine A() {
    do_something();
    yield; // 暂停点
    continue_work(); // 恢复后从这里开始
}

执行机制

暂停时保存执行上下文(寄存器、局部变量等)

恢复时恢复之前保存的上下文

实现"从上次离开的地方继续"

协程类型技术对比

类型

特点

代表

适用场景

有栈协程

独立调用栈,任意深度 yield

Go goroutine

通用性强

无栈协程

无独立调用栈,函数内 yield

C++20、Python generator

性能极致

Async/Await

Promise/Future 语法糖

现代语言主流

开发友好

技术类比

Stackful ≈ 完整的虚拟机

Stackless ≈ 优化的状态机

Async/Await ≈ 统一的编排层

C++20 协程与多核并行实战

核心问题解答

白银级问题:C++20 原生协程如何提高多核并行能力?

Ceph Crimson 项目实践

架构设计

使用 Seastar 框架重写 OSD

I/O 操作建模为协程任务

客户端请求包装成协程任务

并行策略

代码语言:javascript
复制
// 执行示例输出
主线程: 140735273355072
开始分配10个协程任务到线程池...
协程 0 开始处理, 数据: 0, 线程: 123145447251968
协程 1 开始处理, 数据: 10, 线程: 123145452507136
协程 2 开始处理, 数据: 20, 线程: 123145457762304

技术优势

Seastar 采用 share-nothing 架构

避免线程间锁竞争

协程任务分散到不同 CPU 核心

实现高度的并行处理能力

现代协程并行库对比

📂 libfork - 基于无栈协程的工作窃取

架构:无锁细粒度并行库 + 用户空间几何分段栈

调度:Continuation stealing(工作窃取)策略

性能:相比 OpenMP 快 7.2 倍,内存减少 10 倍

📂 concurrencpp - C++ 并发库

特性:通过任务、执行器和协程编写并发应用

执行器thread_pool_executorworker_thread_executor

实践建议与注意事项

关键要点

执行器选择:决定协程任务在哪个线程执行

线程亲和性:注意协程可能在不同线程恢复执行

工作窃取:计算密集型任务的有效负载均衡策略

执行上下文与执行线程解耦

传统线程模型的限制

代码语言:javascript
复制
void traditional_thread_function(int id) {
    int local_var = id;        // 在线程栈上
    std::string data = "data"; // 在线程栈上
    // 变量绑定到特定线程栈,线程结束数据丢失
}

问题根源:执行上下文与执行线程强绑定

C++20 协程的解耦突破

代码语言:javascript
复制
CoroutineTask async_operation(int id) {
    int local_var = id;        // 在协程帧中
    std::string data = "data"; // 在协程帧中
    co_await std::suspend_always{}; // 挂起点
    // 即使线程切换,变量状态保持不变
    std::cout << local_var << ", " << data << std::endl;
    co_return;
}

C++20 协程实现机制

核心机制

1

编译时转换:编译器将协程函数转换为状态机

2

协程帧结构:生成包含所有局部变量的结构体

3

堆分配:默认通过 operator new 分配协程帧

4

状态管理:使用 resume_point 跟踪执行位置

内存模型对比

类型

变量存储

生命周期

线程安全

传统函数

线程栈

函数调用期间

线程绑定

协程

堆上协程帧

协程句柄生命周期

线程间安全迁移

关键洞察:C++20 协程本质是编译器支持的有限状态机,局部变量被"提升"到堆上的状态结构中。

3FS 中 Folly 协程的多核并行实践

Folly 库架构概述

Folly(Facebook Open Source Library)是专为 C++17 设计的组件库,核心宗旨是实用与高效。

ID 分配器的协程应用

核心方法 - tryStartAllocateTask

代码语言:javascript
复制
void tryStartAllocateTask(folly::Executor *exec) {
    // 确保同时只有一个分配任务在运行
    if(!allocating_.exchange(true)) {
        startAllocateTask(exec);
    }
}

设计优势

非阻塞预分配机制

避免每次分配 ID 都访问 FoundationDB

批量分配 4096 个 ID 提高性能

协程实现异步,不阻塞主线程

执行器的关键作用

folly::Executor 是 Folly 核心组件,为协程提供统一执行抽象。

核心功能

任务调度:提交协程任务到合适线程池

执行上下文:提供运行环境和资源管理

异步控制:控制协程启动、暂停和恢复

协程任务启动机制

startAllocateTask 实现

代码语言:javascript
复制
void startAllocateTask(folly::Executor *exec) {
    folly::RequestContextScopeGuard guard;
    allocateTask(weak_from_this()).scheduleOn(exec).start();
}

技术要点

请求上下文管理确保正确性

使用 weak_from_this() 避免循环引用

在指定执行器上调度协程任务

co_await 核心作用详解

三阶段执行模型

1

挂起(Suspend):遇到未完成的 awaitable 时挂起协程

2

等待(Wait):等待 awaitable 对象结果可用

3

恢复(Resume):awaitable 完成时恢复执行并获取结果

执行器选择策略

性能优化指南

CPU 密集型任务:使用线程池执行器

IO 密集型任务:使用 IO 执行器

轻量级任务:使用串行执行器

技术演进总结

从传统电信行业到现代文件系统,协程技术经历了显著的演进。

3FS 通过 Folly 库成功将协程的轻量级并发与线程池的并行性相结合,

实现了在多核架构下的高性能 I/O 处理。

核心认知

协程本身解决并发问题

通过与合适的执行器和线程池配合,才能充分发挥多核并行优势

C++20 协程的执行上下文与执行线程解耦是技术突破的关键

现代协程框架通过工作窃取、负载均衡等策略实现多核高效利用

DeepSeek 3FS源码分析(1) 故障注入

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 后端开发成长指南 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 行业应用对比:从电信到文件系统
  • Folly 协程框架深度解析
    • 设计理念
    • 核心技术特性
  • 新一代系统架构演进:Ceph 案例
    • Seastar 异步框架重构
  • 基础概念深度理解
    • 并发与并行的本质区别
    • 协程的本质价值
  • 协程库的技术演进
    • 传统协程解决方案的局限性
    • 架构演进历史
    • 协程的技术突破
  • 协程实现技术详解
    • 协程的魔法原理
    • 协程类型技术对比
  • C++20 协程与多核并行实战
    • 核心问题解答
    • Ceph Crimson 项目实践
    • 现代协程并行库对比
    • 实践建议与注意事项
  • 执行上下文与执行线程解耦
    • 传统线程模型的限制
    • C++20 协程的解耦突破
    • C++20 协程实现机制
  • 3FS 中 Folly 协程的多核并行实践
    • Folly 库架构概述
    • ID 分配器的协程应用
    • 执行器的关键作用
    • 协程任务启动机制
    • co_await 核心作用详解
    • 执行器选择策略
  • 技术演进总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档