菜菜博士

博士在网络的家

boost.fcontext 为何快?

在 批评 libco 的 上下文切换代码的时候,我曾经说,协程的上下文切换代码,并不需要恢复所有的寄存器。 只需要恢复调用约定里规定的 “非易失性寄存器”。 只有操作系统进行抢占式调度的时候,才需要恢复所有的寄存器。因为抢占式调度,可以在任何时间任何地方中断线程。如果不恢复所有寄存器,则在线程的角度看来就是寄存器的值可以任何时间任何地方突然“自主改变”。 而由用户执行的协程调度则不然。上...

点评 腾讯 的 协程库 libco

序 libco 是 腾讯开源的一个协程库。噱头很大,动不动就是承载了微信后台亿万并发。 敢说个不马上被喷成狗头。但是我偏就是要对这些光有名头没啥技术的东西祛魅。 上一片文章 里,点评了云风的 coroutine库,其中提到了 libco 犯了和云风一样的错。 其实 libco 的错误要比云风还要多的多。 废话不多说,接下来点评 libco libco 错在哪里 首先错误的,仓库上...

点评 云风 的 C库 coroutine

序 在知乎上看到两篇 吹嘘 云风的 coroutine 库的文章。 人啊就是这样的,出名了以后,就是垃圾也有人吹捧。 今天来点评下,云风的 coroutine 到底垃圾在哪里。又或者说,一个优秀的协程,应该优秀在哪里。 云风错在哪里 首先一个优秀的协程库,要做到“自然”。 什么是“自然”呢?就是要做到尽量不改变原来写同步阻塞IO逻辑的时候的代码。 因此引出第一错: 1 协程库...

基于 ucontext + iocp4linux 的超简协程库

虽然说,使用 异步最佳的实践是使用协程。 然而 c++20 的协程并不总是可用的。总不能说,不能更新编译器的地方就不配写代码吧。 因此,我在上一篇文章里说道,可以继续忍受回调地狱。或者使用有栈协程。 当然,如果使用 asio , 那么一切问题都不存在。asio 支持 有栈协程,无栈协程,daff‘s device 协程,还有最基础的,回调模式。 asio 简直就是个“宇宙级” 的异步库...

深入异步IO 第二篇: 正确使用 IOCP,正确设计 proactor

前言 干了那么多年码农,遇到 IO 从来都是写异步代码。从最初直面裸 epoll 写,到后来基本上依赖 asio 写。 最近研究 io_uring 后,试着用 io_uring 把 IOCP 那套 API 实现了。 以前呆在 asio 的舒适区,根本没关注过其他程序员对异步的理解。但是 windows 程序员是最多的,当我研究IOCP的时候, 以前被我忽略掉的那群写 IOCP 的程序员,...

深入理解异步IO

前言 干了那么多年码农,遇到 IO 从来都是写异步代码。从最初直面裸 epoll 写,到后来基本上依赖 asio 写。 最近研究 io_uring 后,试着用 io_uring 把 IOCP 那套 API 实现了。 以前呆在 asio 的舒适区,根本没关注过其他程序员对异步的理解。但是 windows 程序员是最多的,当我研究IOCP的时候, 以前被我忽略掉的那群写 IOCP 的程序员,...

批量提交IO提升性能

在基本完成 iocp4linux 后,我抽空在 rockpi 上测了下性能。 使用仓库里自带的例子 test/web_server/server.cpp,我测得了大约 6000req/s 的性能。 还不错。但是和 PC 上超过十万的性能相比,有那么点。。。弱鸡。 但是我通过 htop 注意到了一点。wrk 压测的时候, cpu1 是 cpu 100%。而且都是 红色的。说明cpu 都...

超轻量级 IOCP 协程库

在 上一篇 文章里,我提出了 iocp4linux 。 在编写 iocp4linux 的过程中,我需要写一些测试代码。 一开始,我随便的找了一个基于 IOCP 的 echo test 和一个简单的 web server。 首先确保这弄来的例子能在 windows 上编译通过。 然后修改 ifdef _WIN32守卫,在 linux 平台上改为使用 iocp.h 头文件。 然后很轻松的...

IOCP 移植到Linux上

序 windows 高性能IO使用的是 proactor 模型,而古代 Linux 上则是 reactor 模型。 因此跨平台的网络库,通常会选择实现为其中一种模型,然后在另一个平台上使用模拟。 比如 asio 使用 proactor 模型。Linux 上使用 epoll 模拟。 又比如 libevent 使用 reactor 模型, windows 上使用 iocp 模拟。 而将 i...

用iouring扩展asio

序 什么叫扩展asio? Beast给Asio增加了 HTTP 协议,不叫扩展asio。那什么叫扩展asio? 举个例子,给 asio 的 socket 对象,基于io_uring提供的 IORING_OP_SHUTDOWN 增加 async_shutdown() 接口,就算扩展asio。因为原本asio并没有异步shutdown功能。而且这个功能并不能靠组合原有的asio接口实现。这就叫...

重叠IO(proactor)是最理想的IO模型

reactor 和 proactor , 这种毫无联系的英文专用名词,很容易把人弄晕。 回归正途,使用精确的汉语描述,而非翻译,则正确的说法是: 多路复用IO 和 重叠 IO 模式。 所谓多路复用,顾名思义,就是好几个IO复用。。。 复用了谁?复用了线程。 它对比的是最传统的UNIX网络编程: 一个连接一个线程。并发就要开多线程。连接多了,线程就多,系统压力非常大。 而多路复用,则可以...

真正的重叠IO

不知有没有人想过,为何微软将windows 的异步API称为“重叠IO”而不是异步IO。 在我看来,重叠IO和异步IO的区别,主要在于发起IO后的动作。 重叠IO, 发起 IO 后,继续执行应用层的逻辑。应用可以选择在任何何时的地点再选择同步(阻塞等待 or 异步等待)IO的结果。 异步IO, 发起 IO 后,立即挂起当前业务逻辑(也就是立即非阻塞等待 IO 结果),回到事件循环,以便执...

不要 new 一个 char 数组当缓冲区

性能调优是一个有魔力的工作。最近研究到了 std::pmr::, 就想着看能否有些老代码能改进改进,提高下性能。 但是,测试的时候,发现还是有一些路径的效率不理想。 经过好久的排查,最终定位代码到这么一行 auto buffer_size = 5*1024*1024; auto buffer = std::make_unique<char[]>(buffer_size); ...

用本机 clang 进行交叉编译

你在 A 平台上编译一份代码,编译出来的结果,在 A 平台无法运行,只能在 B 平台运行。这个就叫交叉编译。 通常使用交叉编译,是因为 B 平台太弱鸡,性能无法胜任编译工作。 因此大部分交叉编译,都是发生在 x86 上为 arm 编译。 为了进行交叉编译,你需要使用一种专门为交叉编译而开发的工具 —— 交叉编译器。 在 Gentoo 上,交叉编译器可以使用工具 ”crossdev” ...

可变模板参数包迭代

序 如果你写了一个模板函数,模板函数里使用了 可变参数。比如你写了个 template<typename... Args> int print(Args... args); 那么,要如何去访问这些可变的参数呢? 方法 1 使用递归法。比如定义2个 print, 一个是单参数的,另一个是 2个模板参数的。 template<typename Arg> in...

把闭包变成函数指针—— trampoline 原理解析

序 十年前,我曾经写过一个让 C 形式的回调函数支持 闭包的小转换工具, 见这。 那时候,我说过,要想把 boost.function 传给 C 接口,那这个 C 接口,必须得带一个 void* user_data 的参数。 比如 typedef int (*callback_t)(int arg1, int arg2, void* user_data); bool registe...

重提类型擦除器

序 十年前,我曾经写过一篇有关类型擦除器的文章, 见这。 十年后,我打算再探讨下类型擦除器。 C++ 对 多态 的支持,在核心层面,就两条: 虚继承 和 模板。 假设你要设计一个 线程池。这个池你可以“投递”各种任务进去。 什么叫”任务“呢? 就是一段代码。于是,早期你想到,可以使用 函数指针。 于是你的 线程池长这样 class threadpool { typedef v...

类型萃取技术:函数签名当模板参数

序 在 上一篇 文章里,我提到了 std::function 的模板参数,是一个叫函数签名 的东西。 什么是函数签名? 比如 main 函数,他的签名是 int(int, char**)。所谓函数签名,是指函数声明去掉 函数名和变量名 后得到的一个精简描述。 这个精简描述,指导编译器如何按”调用约定“产生具体的汇编指令以调用一个函数。 函数名,不过是在最后的 call 指令里,提供了...

不需要void*user_data的闭包封装

一般来说,如果 C api 接受一个回调,通常会额外允许设置一个 void* 的回调参数。 用户可以把一些额外的参数用这个 void* 传给回调函数。 比如 typedef void (*read_function_t)(const char* read_buf, int read_size, void* user_data); int register_read_callback(re...

微软不会起名

微软不会起名,而且名字还具有极大的误导性。 先说类名: 首先,微软的协程原作者,他总是用 Task<> 来命名一个协程函数。但是这就是他犯的第一个错误。 因为只有 await 构成的一系列调用,那条调用链才是一个 “任务”。 也就是 asio 作者所说的 co_spawn. 只有被 spawn 出来的协程,才叫一个任务。而被 await 的函数,不能叫任务,而是”可异步等待返...