stackless or stackfull 协同程式(协程)?

这是一个被多数人们喋喋不休,争论探索的问题,有人说 stackless 协同程式更好性能更快,对内存的使用率更好,似乎都是正确的。

对于我来说,我并不认为 stackfull 协同程式的效率就会低于 stackless 协同程式,所有的数据都应该进行过实际的测试在下结论,或许会更好且有足够的说服力。

C++ 开发人员们,或许更应当抱有更为严谨且专业的态度来审视这个问题,如果我们只是从代码上推导就轻易下决断是一种错误的做法。

stackless 协同程式在 C/C++ 语言上面有多种可行实现,来自 boost 基础框架类库提供的,C/C++ 20 语言标准集成提供的 stackless 协程。

本人没有深度使用过 C/C++ 语言的 stackless 协同程式,毕竟 C/C++ 领域的工程师们,几乎都不乐于追求及应用新潮流技术。

C# 语言提供的 stackless 的协同程式,于 .NET 4.5 Framework 时代被引入,在之前 C# 语言实现协同程式是一种基于早前制定的 C# 语言 yield 规范标准来实现的。

主流的 stackless 的协同程式分为两种实现形式,探索 stackless 的技术原理远比仅仅只会使用它更为重要。

stackless 是由编译器实现的,它本质上是一种多层嵌套的匿名函数及结构,人们访问的变量被存放在具体的结构内。

Yield 函数由驱动器(或者说运行时)调用,每个中断点(Yield)均为一个 Step(步骤),例如(伪代码):

void do() {

// 步骤一

yield return 1;

// 步骤二

yield return 2;

}

此处只是举个小例字,准确的说它是一个状态机结构,结构体字段存储工作时所需的变量,C++ 20 语言标准的 co_await/co_yield 组成的 stackless 协同程式工作原理是类似的。

但我并不关心这一切,stackless 协同程式工作的效率快不快,取决于程式如何去驱动它执行对应的工作流。

例如:我们将 stackless 协同程式的实例(可以理解为一个线程对象)进行工作时,驱动器是一个在线程类循环队列来步进协同程式的工作,可能系统整体的吞吐效能是很低的。

void scheduler_update() {

for (auto& coroutine : coroutines)  {

if (coroutine.state == Coroutine::kRuning) {

coroutine.Next();

}

else {

...

}

}

}

如上述的驱动形式,程式执行效能就比较低下,如果我们为每个分配的协同程式串上链表,当协同程式处理 Yield 时,检查并尝试向 Next 节点进行切换到下一个协同程式,那么效率就会比较高,当然本人只是简单的聊聊,不会涉及到深入的知识点。

void scheduler_execute(auto& coroutine ) {

if (coroutine.state == Coroutine::kRuning) {

coroutine.Next();

}

}

auto* coroutine = ...;

do {

...

scheduler_execute(*coroutine);

coroutine = coroutine->Next; 

...

} while (coroutine);

但,我仍并不建议这样去驱动协同程式,绝大多数应用协同程式的场景都在 Asynchronous(异步的)访问资源处理。

人们是否可以在一个异步的操作被发起时,挂起协同程式,在完成异步的时候恢复协同程式的工作,这样可以省却不必要的开销,大大的提高程式的工作效能。

对于我来说,我并不反感使用 stackless 还是 stackful 协同程式,两者各有优缺点,如果非要说更好的解决方案,或许由编译器复杂实现并支持的 stackless 协同程式是相对最佳的方案,但这里也涉及到,人们会如何去驱动这些协同程式工作。

stackless 协同程式吞吐效能潜在的最大问题是:它是如何驱动这个协同程式进行工作的?如果它是由用户手动在合适的切入点进行驱动的,效能的确是比 stackful 协同程式需要备份并且还原所需 ESP/EBP/EDX/ECX/FS 等寄存器值后JUMP过去的效率要高一些,但这里仅限于假设 stackful 协同程式需要做类似这样的行为。

stackful、stackless 协同程式是两种具有代表性的 coroutine 解决方案,但协同程式切换不仅仅只有这两种解决方案,state-threads (状态线程)的协程切换效率比两者都要高不少,但带来的结果是用户编写的 C/C++ 工程代码晦涩难懂,难以被人维护。

但大多数的 stackless 协同程式执行效能并不一定比 stackful 协同程式的效能更好,因为 stackless 协同程式实现的很多方案内会增加其它一些不必要的东西,比如非必须的运行时用于支持语言平台用户程式很难所使用的功能需求,而牺牲了一定效能。

你可能感兴趣的:(Extension,开发语言)