boost.fcontext 为何快?

Posted on December 18, 2024

批评 libco 的 上下文切换代码的时候,我曾经说,协程的上下文切换代码,并不需要恢复所有的寄存器。 只需要恢复调用约定里规定的 “非易失性寄存器”。

只有操作系统进行抢占式调度的时候,才需要恢复所有的寄存器。因为抢占式调度,可以在任何时间任何地方中断线程。如果不恢复所有寄存器,则在线程的角度看来就是寄存器的值可以任何时间任何地方突然“自主改变”。

而由用户执行的协程调度则不然。上下文切换,一定是代码里“主动”调用某个控制权转移接口实现的。

而这个控制权转移接口,在编译器看来,不过是一次再正常不过的外部函数调用。只要按照约定,准备好寄存器。。 并且在它返回的时候,自主恢复易失性寄存器。

是的没错,调用它的地方,本来就假定易失性寄存器的数值会改变。

因此,在协程的上下文切换函数里,根本没有任何必要恢复易失性寄存器的内容。只需要恢复非易失性寄存器即可。

为了验证我的设想,我写了一个 zcontext 进行了验证。 这个 zcontext 已提交进 uasync 代码仓库,详情看 commit

不过我这上下文切换,只恢复了8个非易失性通用寄存器。对16 个 MMX 寄存器,还有 AVX 寄存器,都没有进行恢复。 不知道会不会出问题。于是就仔细的研读了 boost.context 的汇编代码。希望研究下怎么恢复 SSE 和 AVX 寄存器。

注:boost.context 里负责上下文切换的 汇编写的 api 叫 fcontext 。

结果发现 fcontext 的做法和我的不能说完全一样,简直是一模一样。

无非是我用了 一排 push + 一排 pop 做的切换,而它的代码使用了 mov 指令。但都是等价的。

也只恢复了非易失性通用寄存器和 2个 80387状态寄存器。

这下总算完全理解为啥 boost.context 称自己的性能天下无敌了。

因为确实没有比这更快的上下文切换方式了。

由此可见,libco 的作者还真是水货一个。他引以为傲的那段汇编代码,就是一坨羊粪。

Comments