0

我是使用 conditional_variables 的新手,所以我很容易在这里做一些愚蠢的事情,但是当我使用 boost 线程而不是直接调用函数时,我得到了一些奇怪的性能。如果我将在 func 上创建 boost 线程的行更改为直接调用 func,则代码会更快地运行几个命令。我已经尝试使用源伪造的 boost 线程池软件,它没有任何区别......

这是代码:

#include <boost/thread.hpp>


using namespace boost;

condition_variable cond;
mutex conditionalMutex;
int numThreadsCompleted = 0;
int numActiveThreads = 0;

void func()
{
  {
    lock_guard<mutex> lock(conditionalMutex);
    --numActiveThreads;
    numThreadsCompleted++;
  }
  cond.notify_one();
};


int main()
{
  int i=0;
  while (i < 100000)
    {
      if (numActiveThreads == 0)
        {
          ++numActiveThreads;
          thread thd(func);
          //Replace above with a direct call to func for several orders of magnitude
          //performance increase...
          ++i;
        }
      else
        {
          unique_lock<mutex> lock(conditionalMutex);
          while (numThreadsCompleted == 0)
            {
              cond.wait(lock);
            }
          numThreadsCompleted--;
        }
    }
  return 0;
}
4

3 回答 3

1

性能肯定比直接调用函数差很多。您启动一个线程,然后等待该线程结束。即使您将启动线程的开销减少到零,您也可以与该线程进行通信。而且您将至少有一个上下文切换,并且由于您的 func() 基本上什么都不做,因此开销成为主要因素。在 func() 中添加更多有效负载,比率会发生变化。如果要做的事情很少,就在找到这个事情的线程上做。

顺便说一句:您有一个竞争条件,因为您在没有锁定互斥锁的情况下写入 numActiveThreads。上面的代码归结为:

int main()
{
    int i=0;
    while (i < 100000)
    {
        thread thd(func);
        thd.join();
        ++i;
    }

    return 0;
}

确实没有理由说这应该比:

int main()
{
    int i=0;
    while (i < 100000)
    {
        func();
        ++i;
    }

    return 0;
}
于 2012-08-17T10:38:01.140 回答
0

除了创建和销毁线程的开销之外,分支预测可能会导致性能差异。

在没有线程的情况下,if 语句始终为真,就像numActiveThreads0每个循环迭代的开始和结束时一样:

while (i < 100000)
{
  if (numActiveThreads == 0) // branch always taken
  {
    ++numActiveThreads; // numActiveThreads = 1
    func();             // when this returns, numActiveThreads = 0
    ++i;                
  }
}

这导致:

  • 分支预测永远不会失败。
  • 没有线程创建/销毁的开销。
  • 没有时间阻塞等待获取conditionalMutex

使用线程,numActiveThreads可能会或可能不会0在顺序迭代中。在我测试的大多数机器上,都观察到了可预测的短模式,每次迭代时 if 语句和 else 语句之间的分支交替出现。但是,有时在顺序迭代中选择 if 语句。因此,时间可能会浪费在:

  • 分支预测失败。
  • 线程的创建和销毁。如果创建和销毁是并发的,则可能在底层线程库中发生同步。
  • 阻塞等待获取conditionMutex或等待cond
于 2012-08-16T04:12:57.070 回答
0

您正在创建和销毁线程,这些线程通常被实现为一些较低级别的操作系统构造,通常是某种轻量级进程。这种创建和破坏可能代价高昂。

最后,你基本上在做

  1. 创建线程
  2. 等待线程退出

一遍又一遍地。这意味着创建/销毁,并且您每次都在这样做,因此成本会增加。

于 2012-08-16T01:36:59.600 回答