tinyCoroLab Docs
直通tinyCoroLab源码直通tinyCoro源码
latest
latest
  • 🚀开启你的tinyCoroLab之旅!
  • 📮致读者
  • 👊C++协程入门
    • 协程初探
    • 有栈协程VS无栈协程
    • C++协程入门实践
    • 从编译器视角揭秘 C++ 协程
    • 协程调用优化
  • 🌟认识io_uring
  • 📖tinyCoroLab实验介绍
  • 📘Lab1 构建协程任务封装
    • Lab1 构建协程任务封装
    • Lab1 实验解析
  • 📗Lab2 构建任务执行引擎
    • Lab2a 构建任务执行引擎engine
    • Lab2a 实验解析
    • Lab2b 构建任务执行引擎context
    • Lab2b 实验解析
  • 📙Lab3 封装异步I/O执行模块
  • 📕Lab4 构建基础协程同步组件
    • Lab4pre 如何构建协程同步组件
    • Lab4a 构建基础协程同步组件event
    • Lab4a 实验解析
    • Lab4b 构建基础协程同步组件latch
    • Lab4b 实验解析
    • Lab4c 构建基础协程同步组件wait_group
    • Lab4c 实验解析
    • Lab4d 构建基础协程同步组件mutex
    • Lab4d 实验解析
  • 📓Lab5 构建进阶协程同步组件
    • Lab5a(选做) 构建进阶协程同步组件when_all
    • Lab5a 实验解析
    • Lab5b 构建进阶协程同步组件condition_variable
    • Lab5b 实验解析
    • Lab5c 构建进阶协程同步组件channel
    • Lab5c 实验解析
  • ✨tinyCoro Bonus Lab
  • 🎯tinyCoro悬赏令
  • ⚔️面试实战
    • 面试实战
    • tinyCoro面试相关问题
  • 🚩实验总结-终点亦是起点
  • 🪐番外杂谈
    • 从编译器视角揭秘 C++ 协程
    • 协程调用优化
  • 📌更新日志
Powered by GitBook
On this page
  • 📖知识回顾
  • C++20 协程
  • C++20 新标准的使用
  • io_uring 与 liburing
  • 基于 eventfd 的轻量级事件通知机制
  • 协程式编程思维
  • 易拓展代码设计
  • 标准的项目组织
  • 🛸展望未来
  • 结语

🚩实验总结-终点亦是起点

在进行最后的实验总结前,我想先恭喜你正式通过了 tinyCoroLab 设下的重重考验🎉🎉🎉!你已经成功利用 C++ 协程和 liburing 搭建出了属于自己的异步协程库,如果你有认真对代码做优化,那么 lab3 的测试结果一定会让你见识到协程结合 liburing 的强大之处。

但 tinyCoroLab 实验的结束并不代表你的学习之旅到达终点,恰恰相反,tinyCoroLab 仅仅只是为读者展示了如何用协程结合 liburing 搭建高性能的异步执行引擎,对于实际使用例如搭建 HTTP 或 rpc 服务器均未涉及,这部分工作交由你来实现,另外你还可以尝试对 tinyCoroLab 的核心执行引擎进行设计优化来进一步提升性能,总之,需要你做的事还可以很多很多。

本篇实验总结将会回顾 tinyCoroLab 涉及到的重要知识点,并对 tinyCoroLab 的未来工作做出展望,以此为愿意拓展 tinyCoroLab 的读者提供参考。

📖知识回顾

C++20 协程

在完成 tinyCoroLab 后想必读者对 C++20 协程的应用已经了如指掌,从深入剖析原理的角度讲协程是复杂的,但从使用者的角度将协程就像是一个可被中途中断并恢复的特殊函数,作为协程库开发者便是要规定协程函数何时以及如何中断和恢复,这种机制完美契合异步 IO,通过将协程和异步 IO 结合可以用同步的写法来替换丑陋的异步回调写法并获得异步 IO 的高性能。

但任何技术都存在弊端,因为需要支持复杂的执行跳转以及通过堆内存维护协程的运行状态,协程调用相比普通调用十分昂贵,主要体现在动态内存分配,如果可以优化协程的默认内存分配,那么协程库的性能将会有巨大的提升。

C++20 新标准的使用

C++20 标准为 C++ 添加了四大特性:

  • module

  • coroutine

  • concept

  • range

除开大家熟知的coroutine外,tinyCoroLab 还涉及了concept,这是 C++ 模板的又一重大变革。

模板编程在 C++ 项目的开发中占据了十分重要的地位,通过合理的使用模板可以大大减小开发者的工作量,增加代码复用性,而concept提供了一种更清晰、更精确的方式来约束模板参数,提高了代码可读性和编译错误提示的准确性,对于实际项目开发的编程规范也有很好的促进作用。tinyCoroLab 的代码中涉及大量模板代码并且普遍使用了concept,读者可以从中学习 concept 在项目中的具体应用。

io_uring 与 liburing

io_uring 是 Linux 官方人员从 2018 年开始一直致力于发展和推广的异步 IO 技术,其本身利用mmap将内核数据结构映射到用户态从而避免昂贵的系统调用开销,因此 io_uring 拥有极高的性能。

性能并不是 io_uring 的主要优势,aio 和 epoll 同样也有极高的性能,最重要的是 io_uring 对 io 的支持十分广泛,上至网络 IO 下至文件存储 IO,妥妥的六边形战士,而 aio 和 epoll 的使用场景是被严格受限的,因此 io_uring 更像是 Linux 官方对异步 IO 技术定下的新规范。

io_uring 本身只涉及 3 个系统调用但想用好绝非易事,官方为了简化 io_uring 的使用特地开发了 liburing 库,而 liburing 优雅的设计使得用户可以在享有 io_uring 高效性的同时获得极强的拓展性,不论是哪种 IO 其调用流程均是相似的,相信读者在 lab3 一定深有体会。

基于 eventfd 的轻量级事件通知机制

现有的网络库绝大多数都使用了事件循环的概念,工作线程在一遍遍循环中感知事件,如果不存在事件那么暂时陷入阻塞,比如 epoll,而 io_uring 的一种常见使用方式便是事件循环。

tinyCoro 用到了 io_uring 和事件循环,但并没有使用 io_uring 的事件循环,因为 tinyCoro 要处理的不仅仅有 IO 事件,还有协程计算任务,基于此,tinyCoro 将事件循环转移至轻量的 eventfd,在没有任何任务的前提下线程将阻塞在 eventfd 读操作从而让出 cpu,liburing 在产出 IO 完成事件时会自动向 eventfd 写值,当任务队列新增协程任务时可以手动向 eventfd 写值,这样都会导致线程恢复运行继续执行任务。这样我们就正式用 eventfd 搭建了一个简单高效的事件通知机制。

💡eventfd 的读写涉及到系统调用会产生很大开销吗? eventfd 的读写是非常轻量的系统调用,读者可以在本地尝试循环读写多次测量其性能,据我本人测试结果一次完整读者耗时不超过 1us,从目前设计来看,这点开销是绝对不会产生 tinyCoroLab 的性能瓶颈。

协程式编程思维

协程除了与异步 IO 搭配外,其本身可被暂停和恢复的机制可以有效的改善线程池模型。在 lab4 和 lab5 中,我们搭建的协程同步组件会使得协程在被阻塞时主动让出执行权,这样线程可以继续执行下一个任务。

上述便是协程式编程思维的典型应用案例,在协程式编程思维下将线程阻塞转移到了协程阻塞从而增大 CPU 的利用率,读者可以在后续对 tinyCoro 的拓展中利用该思维搭建更多高效的组件。

易拓展代码设计

设计模式作为软件工程界的圣经,对于软件项目尤其是大型软件项目的开发有着极其重要的作用,如果只是个人 demo 项目那确实代码风格可以很随意,一旦参与到大型项目尤其是更新迭代较频繁的项目,遵循设计模式可以在在保证代码质量的同时简化后续功能新增及维护工作。

设计模式涉及的概念很多且在 tinyCoroLab 的代码设计中也有所体现,比如scheduler的单例模式,uring_proxy的代理模式,scheduler与context之间交互的低耦合,dispatcher的模板类设计等等,这些设计都是为了保证一个最重要的原则:易拓展性,这样 tinyCoroLab 的后续更新才会更加轻松。

当然软件的拓展性是一个很庞大的话题,希望读者可以在 tinyCoroLab 中有所接触并将该思想应用于未来的开发中。

标准的项目组织

tinyCoroLab 的项目组织参照了优秀的高 star 开源 C++ 项目,在做完实验后读者可以查看 tinyCoroLab 是如何利用 cmake 组织构建以及项目的文件结构,最为重要的是读者应当学会将googletest和googlebenchmark集成到项目中,从而为项目编写功能和性能测试。

🛸展望未来

tinyCoroLab 还有很多需要改进的地方,如果读者愿意继续拓展个人实现的 tinyCoro,那么我会为你列出以下 ider 供你参考。

  • sqe 消费限制: tinyCoro 在发起 IO 前会先获取 sqe,而 io_uring 的可用 sqe 是有限制的,如果可用 sqe 消费完那么 tinyCoro 会得到空指针 sqe 并且操作该 sqe 就会引发 core dump,不过由于实际使用中 io_uring 的 sqe 是在单线程中顺序获取且默认配置项为 io_uring 设置了上万的 sqe 容量,所以 sqe 耗尽的情况并不常见,但读者仍然可以对该部分缺陷添加解决方案。

  • 高效的负载均衡: scheduler 对各个 context 的任务派发逻辑是由 dispatcher 决定的,目前 tinyCoro 仅实现了 round-robin 逻辑的 dispatcher,读者可以拓展 dispatcher 的模板类从而实现更高效的负载均衡逻辑。

  • 基于协程同步组件的执行引擎: 因为实验设计的原因协程同步组件在 lab4 和 lab5 才有涉及,也因此执行引擎的设计与协程关联不大,但实际上读者若想实现新的执行引擎,其中需要用到例如条件变量的等待唤醒功能,那么便能利用协程同步组件家族中的 condition_variable 来实现。

  • 网络层协议拓展: tinyCoroLab 默认添加了 tcp 支持,读者可尝试添加 HTTP 和 rpc 支持。

  • 多线程执行引擎优化: 目前 tinyCoroLab 的多线程执行引擎是有多个 context 组成并由 scheduler 负责任务调度,各个 context 之间彼此独立且均持有一个工作线程和一个 io_uring 实例,这样的设计可以有效避免线程竞争带来的消耗,如果读者想要优化该部分,可以参考 golang 的 gmp 设计。

结语

tinyCoroLab 的更新会持续进行,并致力于打造对标工业标准的代码质量和性能,如果您对该项目有任何新奇的想法,欢迎与我进一步交流!

PrevioustinyCoro面试相关问题Next🪐番外杂谈

Last updated 15 days ago