首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >无堆栈协同与堆叠协同有什么不同?

无堆栈协同与堆叠协同有什么不同?
EN

Stack Overflow用户
提问于 2015-03-11 01:57:33
回答 1查看 32K关注 0票数 86

背景:

我之所以问这个问题,是因为我目前有一个有很多(数百到数千个)线程的应用程序。这些线程中的大部分时间都处于空闲状态,等待将工作项放入队列。当一个工作项可用时,它将通过调用一些任意复杂的现有代码来处理。在某些操作系统配置中,应用程序与控制最大用户进程数量的内核参数发生碰撞,因此我想尝试减少工作线程数量的方法。

我提议的解决方案:

这似乎是一种基于协同线的方法,我用一个协同线替换每个工作线程,这将有助于实现这一点。然后,我可以让一个工作队列得到一个实际(内核)工作线程池的支持。当一个项被放置在一个特定的协同队列中进行处理时,一个条目将被放置到线程池的队列中。然后,它将恢复相应的协同线,处理其排队的数据,然后再次挂起它,释放工作线程来执行其他工作。

实现详细信息:

在思考如何做到这一点时,我很难理解无堆栈和堆叠协同机制之间的功能差异。我有一些使用堆叠协同使用Boost.Coroutine库的经验。我发现从概念层次上理解它相对容易:对于每个协同线,它维护CPU上下文和堆栈的副本,当您切换到coroutine时,它切换到保存的上下文(就像内核模式调度器一样)。

我不太清楚的是,一个无堆栈的协同线与此有何不同。在我的应用程序中,与上述工作项排队相关的开销是非常重要的。我所见过的大多数实现,如the new CO2 library,都表明无堆栈协同服务提供了更低开销的上下文交换机。

因此,我想更清楚地理解无堆栈和堆叠协同效应之间的功能差异。具体来说,我认为这些问题:

  • References like this one建议,区别在于您可以在一个堆叠的和无堆栈的协同条件下获得/恢复。是这种情况吗?是否有一个简单的例子,我可以在一个堆积如山的协同线,而不是在一个无堆栈的?
  • 使用自动存储变量(即“堆栈上”的变量)是否有任何限制?
  • 我可以从无堆栈的协同线调用哪些函数,有什么限制吗?
  • 如果没有保存无堆栈协同线的堆栈上下文,那么当协同线运行时,自动存储变量会去哪里?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-11 14:38:42

首先,感谢您查看CO2 :)

Boost.Coroutine doc很好地描述了堆积如山的协同工作的优点:

堆栈 与无堆栈的coroutine不同,可以将堆叠的coroutine从嵌套的堆栈框架中挂起。在之前挂起的代码中,执行将在完全相同的位置恢复。使用无堆栈的协同线,只有顶层的例程才可能被暂停。由顶级例程调用的任何例程本身都不能挂起。这禁止在通用库中的例程中提供挂起/恢复操作。 一级延拓 一级延续可以作为参数传递,由函数返回,并存储在以后使用的数据结构中。在某些实现(例如C#产量)中,不能直接访问或直接操作延续。 没有堆栈和一流的语义,就无法支持一些有用的执行控制流(例如协作多任务处理或检查点)。

这对你意味着什么?例如,假设您有一个接收访问者的函数:

代码语言:javascript
复制
template<class Visitor>
void f(Visitor& v);

要将其转换为迭代器,使用堆叠的协同线,您可以:

代码语言:javascript
复制
asymmetric_coroutine<T>::pull_type pull_from([](asymmetric_coroutine<T>::push_type& yield)
{
    f(yield);
});

但是对于无堆栈的协同线,没有办法做到这一点:

代码语言:javascript
复制
generator<T> pull_from()
{
    // yield can only be used here, cannot pass to f
    f(???);
}

一般来说,堆积如山的协同作用比无堆栈的协同作用更强大。那我们为什么要无堆栈的协同线呢?简短的回答:效率。

Stackful通常需要分配一定数量的内存来容纳它的运行时堆栈(必须足够大),并且上下文切换比无堆栈的要昂贵,例如Boost.Coroutine需要40个周期,而CO2在我的机器上平均只需要7个周期,因为没有堆栈的coroutine需要恢复的唯一东西是程序计数器。

也就是说,有了语言支持,堆栈可能也可以利用编译器计算的堆栈的最大大小,只要协同线中没有递归,那么内存的使用也可以得到改善。

说到无堆栈协同,请记住,这并不意味着根本没有运行时堆栈,它只意味着它使用与主机端相同的运行时堆栈,所以您也可以调用递归函数,只是所有的递归都将发生在主机的运行时堆栈上。相反,对于堆叠的协同,当您调用递归函数时,递归将发生在coroutine自己的堆栈上。

回答以下问题:

  • 使用自动存储变量(即“堆栈上”的变量)是否有任何限制?

不是的。这是CO2仿真的局限性。在语言支持下,协同线可见的自动存储变量将放置在协同线的内部存储中。注意,我的重点是“coroutine可见的”,如果coroutine调用一个在内部使用自动存储变量的函数,那么这些变量将放在运行时堆栈上。更具体地说,无堆栈协同只需要保留在恢复后可以使用的变量/临时变量。

为了清楚起见,您也可以在二氧化碳的协同体内使用自动存储变量:

代码语言:javascript
复制
auto f() CO2_RET(co2::task<>, ())
{
    int a = 1; // not ok
    CO2_AWAIT(co2::suspend_always{});
    {
        int b = 2; // ok
        doSomething(b);
    }
    CO2_AWAIT(co2::suspend_always{});
    int c = 3; // ok
    doSomething(c);
} CO2_END

只要定义不先于任何await

  • 我可以从无堆栈的协同线调用哪些函数,有什么限制吗?

不是的。

  • 如果没有保存无堆栈协同线的堆栈上下文,那么当协同线运行时,自动存储变量会去哪里?

上面回答说,一个无堆栈的协同器并不关心被调用函数中使用的自动存储变量,它们只会放在正常的运行时堆栈中。

如果你有任何疑问,只要检查二氧化碳的源代码,它可能会帮助你理解引擎盖下的机械原理;)

票数 73
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28977302

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档