代码之家  ›  专栏  ›  技术社区  ›  Nikhil

Intel线程构建块并发队列:在pop上使用pop()if_present()。

  •  4
  • Nikhil  · 技术社区  · 15 年前

    使用阻塞调用有什么不同 pop() 相比之下,

    while(pop_if_present(...)) 
    

    哪一个比另一个更好?为什么?

    我正在寻找一个更深入的了解,在投票和 while(pop_if_present(...)) 关于让系统为你做这件事。这是一个相当普遍的主题。例如,使用 boost::asio 我可以做一个 myIO.run() 哪个模块或执行以下操作:

    while(1) 
    {
    myIO.poll()
    }
    

    一种可能的解释是调用 while(pop_if_present(…)) 会很忙,所以这很糟糕。但必须有人或其他人对异步事件进行投票。当它被委托给操作系统或库时,为什么这样做更便宜呢?这是因为操作系统或库智能地进行轮询吗?例如,进行指数退避?

    2 回复  |  直到 15 年前
        1
  •  3
  •   Michael Burr    15 年前

    英特尔的tbb库是开源的,所以我看了一下……

    看起来像 pop_if_present() 基本上检查队列是否为空,如果为空则立即返回。如果没有,它会尝试将元素放在队列的顶部(这可能会失败,因为另一个线程可能会出现并占用它)。如果未命中,则执行 atomic_backoff “再次检查前暂停。这个 原子退避 只会在它被调用的前几次旋转(每次将其旋转循环计数翻倍),但是在经过一定的暂停之后,它会向OS调度器屈服,而不是旋转,因为它已经等待了一段时间,所以它可能会做得很好。

    为平原 pop() 函数,如果队列中没有任何内容将执行 原子退避 等待队列中有它得到的内容。

    注意,至少有两件有趣的事情(对我来说)与此有关:

    • 这个 () 函数执行spin等待(直到某个点),等待队列中出现某个内容;它不会向操作系统屈服,除非它必须等待一小段时间。所以,正如你所期望的,没有太多的理由让你自己打电话。 pop_如果存在()) 除非你还有别的事要做 pop_如果存在())

    • 什么时候? () 确实屈服于操作系统,它只是简单地放弃了它的时间片。它不会阻塞同步对象上的线程,当一个项目被放置在队列上时,同步对象上的线程会被发出信号——它似乎进入了睡眠/轮询周期,以检查队列中是否有要弹出的内容。这让我有点惊讶。

    用一粒盐来做这个分析…我用于此分析的源可能有点旧(实际上来自concurrent_queue_v2.h和.cpp),因为最近的concurrent_queue具有不同的API-没有 () pop_如果存在()) 只是一个 try_pop() 最新功能 class concurrent_queue 接口。旧接口已移动(可能有所更改)到 concurrent_bounded_queue 班级。当库构建为使用OS同步对象而不是繁忙的等待和轮询时,似乎可以配置较新的并发队列。

        2
  •  2
  •   Nikolai Fetissov    15 年前

    while(pop_if_present(...)) 你在做暴力 busy wait (也称为旋转)在队列上。当队列为空时,您将浪费周期,使CPU保持繁忙状态,直到某个项目被运行在不同CPU上的另一个线程推送到队列中,或者操作系统决定将CPU分配给其他可能不相关的线程/进程。

    你可以看到如果你只有一个CPU,这会是多么糟糕——生产者线程将无法推动,从而停止消费者旋转,直到消费者的 time quanta 加上A的开销 context switch .显然是个错误。

    对于多个CPU,如果操作系统选择(或强制)生产者线程在不同的CPU上运行,这可能会更好。这是 spin-lock -直接建立在特殊处理器指令上的同步原语,如 compare-and-swap load-linked/store conditional 通常在操作系统内部用于在中断处理程序和内核其余部分之间进行通信,并构建更高级别的构造,如 semaphores .

    用阻塞 pop() ,如果队列为空,则进入 睡眠等待 ,即要求操作系统将消费者线程放入 不可调度的 状态,直到从另一个线程发生事件(推到队列中)。这里的关键是处理器可用于其他(希望有用)工作。TBB实现实际上很难避免睡眠,因为它很昂贵(进入内核、重新安排时间等),其目标是优化队列不为空且可以快速检索项目的正常情况。

    但是选择很简单- 总是睡眠等待 ,即做阻塞 () 除非 你必须 busy wait(即在实时系统、操作系统中断上下文和一些非常专用的应用程序中)。

    希望这有点帮助。