【问题标题】:fastest way to wake up a thread without using condition variable不使用条件变量唤醒线程的最快方法
【发布时间】:2011-08-01 08:54:46
【问题描述】:

我试图通过设置后台线程来加速一段代码来解决一个特定的任务。当需要解决我的任务时,我想唤醒这些线程,完成工作并再次阻止它们等待下一个任务。任务总是一样的。

我尝试使用条件变量(以及需要与它们一起使用的互斥锁),但我最终降低了我的代码速度,而不是加快了它的速度;主要是因为调用所有需要的函数都非常昂贵 (pthread_cond_wait/pthread_cond_signal/pthread_mutex_lock/pthread_mutex_unlock)。

使用线程池(我也没有)没有意义,因为它是一个过于通用的构造;在这里,我只想解决我的具体任务。根据实现,我还会为队列支付性能损失。

您对不使用mutexcon_var 的快速唤醒有什么建议吗?

我正在考虑设置线程,例如读取atomic variable 的计时器;如果变量设置为 1,线程将完成这项工作;如果设置为 0,它们将进入睡眠状态几微秒(我会从微秒睡眠开始,因为我想避免使用可能对 CPU 来说太贵的spinlocks)。你怎么看待这件事?任何建议都非常感谢。

我正在使用 Linux、gcc、C 和 C++。

【问题讨论】:

  • 如果您的性能要求对于现有的互斥锁/条件变量方法来说过于极端,那么您已经到了需要消耗一点 CPU 旋转以完成更多工作的阶段,然后再重新使用互斥体/条件变量。微秒睡眠可能无法按您预期的那样工作:如果您的进程没有被取消调度,那么 CPU 无论如何都不会得到其他工作,如果是这样,您的延迟可能会飙升。
  • 嗨托尼。我有一台多核 NUMA 机器。在这种情况下我不应该进行任何上下文切换,这是真的吗?我在没有任何特定设置或配置的情况下创建线程...您认为需要任何特殊设置来避免上下文切换吗?
  • @Abruzzo:有很多因素,主要是调度逻辑随着 Linux 内核版本的变化而改变。但是,一般来说,如果你告诉调度程序你无事可做并且它有一些东西在等待,我不会打赌它会让你留在身边(如果你的延迟时间显然是时间片内的更好机会)。通过任何认真的调整,聪明的钱都花在使用您的实际硬件、编译器、任务大小、争用率、数据流、内核版本等来实施替代方案和基准测试。
  • 如果任务适合,我会提出使用内部状态机的无锁算法。
  • @Blagovest 你能详细说明一下吗?您的意思是围绕保持状态的变量进行while循环吗?我不明白如何将它们用于我的问题,即让我的线程对某些事件真正“反应”,并告诉他们尽快开始执行我的任务。

标签: c++ c multithreading performance atomic


【解决方案1】:

为了在多线程应用程序中获得性能,生成与 CPU 一样多的线程,而不是为每个任务创建一个单独的线程。否则,您最终会因上下文切换而产生大量开销。

您也可以考虑使您的算法更线性(即通过使用非阻塞调用)。

【讨论】:

  • 线程的数量已经和CPU的数量一样。您是否知道任何设置或强制设置/属性以确保 100% 我没有任何上下文切换?我有一个多核也是 NUMA 机器。
  • 没有任何上下文切换意味着停止使用线程 :-) 考虑让你的算法成为非阻塞的。例如,看看 Nginx 和 Lighttpd 服务器是如何在没有线程的情况下为每个传入连接制作的。
  • @Abruzzo Forte e Gentile 您需要一个实时操作系统来提供这样的保证。如果您在 linux 上使用 SCHED_FIFO “实时”调度程序,除了将线程固定到特定 CPU(通过进程关联)之外,您可以非常接近,请参阅 sched_setscheduler 的手册页。如果您的逻辑可以承受,您可以将项目排队并将它们分批发送给工作人员,我从排队 10 个项目并触发 pthread_cond 变量与为每个项目触发它获得的吞吐量增益是显着我>
  • 我的任务有一定程度的并行性,我想用额外的线程加以利用。我可以使用无锁概念,但我需要让我的线程已经在后台创建并等待快速启动。
  • 考虑让你的线程在不同的数据上做同样的事情,而不是一个单独的线程专用于一个任务。至于任务本身,请考虑将它们分成小部分,以便您可以异步处理它们并在内部保持它们的状态。
【解决方案2】:

这些功能应该很快。如果它们占用了您的大部分时间,则很有可能您尝试切换线程过于频繁。

尝试缓冲一个工作队列,并在积累了大量工作后发送信号。

如果由于任务之间的依赖关系而无法做到这一点,那么您的应用程序根本不适合多线程。

【讨论】:

  • 这无论如何都会有效地发生......即,如果原始事件的处理速度不够快,接下来的几个会被缓冲。所以,似乎问题是前沿的延迟越来越好......
  • @Tony:接下来的几个没有被缓冲,甚至没有被生成,因为主线程被阻塞了。这是一种减少给定工作集的线程库调用总数的策略。
  • @Potatoswatter:我只是说,在等待进一步事件的通知之前,一个消费线程应该已经清空了所有可用请求的队列(即不一个一个地“吃掉”它们);我同意如果应用程序不是这样设计的,这是一个问题。我不确定,但我认为问题更多是关于为频繁但处理简单的事件获得一致的低延迟 - 例如处理微秒,相隔微秒。阿布鲁佐...?
  • @Tony。这是正确的。我什至没有使用队列,因为我的任务被完美地分割成一些频繁但微不足道的事情,我希望由 3 或 4 个线程快速处理。所以我的任务可以拆分为 4 个线程的 4 个任务;如果那些在“on task”事件中运行/唤醒的速度比我的延迟低。
  • @Tony:延迟不应该受lock时间的影响,因为它是在signal之后被主线程调用的。剩下的unlock 很快,cond_signal 和来自cond_wait 的返回序列应该与 pthread 中的任何唤醒路径一样快。这并不是说没有更快的方法,但除非确定延迟是目标,否则就足够了。至于一次一个,我的经验是队列可能一次一个,除非您设置最小大小,否则会在lock/unlock/signal/cond_wait 中产生多余的时间。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-15
  • 2017-11-09
  • 1970-01-01
  • 2018-10-13
  • 2011-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多