【问题标题】:C++ pthread'ed process running slower than single thread issueC++ pthread 的进程运行速度比单线程问题慢
【发布时间】:2014-10-28 03:22:15
【问题描述】:

我试图在多个 pthread 上运行一个函数以提高效率和运行时间。该函数执行大量矩阵计算和打印语句。但是,当我运行测试以查看性能改进时,单线程代码运行得更快。

我的测试如下:

-对于单线程:运行一个调用函数的for循环1:1000。

-对于多线程:生成 100 个 pthread,有一个包含 1000 个项目的队列和一个 pthread_cond_wait,并让线程运行该函数,直到队列为空。

这是我的 pthread 代码(单线程只是一个 for 循环):

# include <iostream>
# include <string>
# include <pthread.h>
# include <queue>

using namespace std;
# define NUM_THREADS 100

int main ( );
queue<int> testQueue;
void *playQueue(void* arg);
void matrix_exponential_test01 ( );
void matrix_exponential_test02 ( );
pthread_mutex_t queueLock;
pthread_cond_t queue_cv;

int main()
{
    pthread_t threads[NUM_THREADS];
    pthread_mutex_init(&queueLock, NULL);
    pthread_cond_init (&queue_cv, NULL);

    for( int i=0; i < NUM_THREADS; i++ )
    {
       pthread_create(&threads[i], NULL, playQueue, (void*)NULL);
    }

    pthread_mutex_lock (&queueLock);
    for(int z=0; z<1000; z++)
    {
        testQueue.push(1);
        pthread_cond_signal(&queue_cv);
    }
    pthread_mutex_unlock (&queueLock);

    pthread_mutex_destroy(&queueLock);
    pthread_cond_destroy(&queue_cv);
    pthread_cancel(NULL);*/
    return 0;
}


void* playQueue(void* arg)
{
bool accept;
while(true)
{
    pthread_cond_wait(&queue_cv, &queueLock);
    accept = false;
    if(!testQueue.empty())
    {
        testQueue.pop();
        accept = true;
    }
    pthread_mutex_unlock (&queueLock);
    if(accept)
    {
        runtest();
    }
}
pthread_exit(NULL);
}

我的直觉告诉我,多线程版本应该跑得更快,但事实并非如此。有什么原因,还是我的代码有问题?我在 Windows 上使用 C++,必须下载一个库才能使用 pthread。

【问题讨论】:

  • 您的playQueue 被声明为返回一个指针,但代码不返回任何值。那是未定义的行为。此外,您的代码中有一个 *//* 不匹配 另外,您有 100 个 cpu(核心)吗?否则,即使您的程序是并行的,大多数线程也会在队列中等待并浪费同步开销。
  • playQueue 实际上并没有做任何工作。您正在测量创建和拆除线程的纯开销。
  • @IgorTandetnik 抱歉我抄错了,但我编辑了这个问题。该线程应该执行函数 runtest() ,其中包含所有数学。
  • 在担心让您的程序快速运行之前,您需要先使其正确。有两个未定义行为的来源:(1) playQueue 调用 pthread_cond_wait 而不首先锁定互斥体,以及 (2) main 销毁互斥体和条件变量而不等待线程完成。
  • 你不应该运行很多线程。 100 几乎总是过高。尝试运行比核心多一点的线程(即在 Linux 上grep processor /proc/cpuinfo| wc -l...的结果)。所以尝试使用 5、10、15 和 20 线程。

标签: c++ multithreading performance pthreads


【解决方案1】:

首先,您的代码是以一种在任何时候都只会运行一个线程的方式编写的(您的互斥锁在线程工作的整个过程中都被锁定)。因此,在最佳,您可以期望您的代码与单线程版本一样快。

此外,所有线程每次都读取和写入相同的内存。这样你就可以强制你的 CPU 内核同步它们的缓存,这意味着实际上总线上的负载更多比单个线程造成的负载。由于您没有做任何计算上昂贵的事情,内存带宽很可能是您的实际瓶颈,因此缓存同步增加的总线负载会减慢您的程序。请查看http://en.wikipedia.org/wiki/False_sharing 了解更多信息。

【讨论】:

  • 我对问题进行了编辑,因为我忘记了一个主要部分,即它调用的函数。所以函数 runtest() 是所有数学运算发生的地方,所以它应该能够自己运行。唯一一次锁定互斥锁是在它更新队列时。
  • 取决于测试需要多长时间,这可能仍然是同一个问题。如果您的测试可以在几乎没有内存访问的情况下执行,那么测试可能仍然太快而无法产生任何影响。一个内核可以执行数百条算术指令,而另一个内核则使用内存总线。在这种情况下,无论您是否包含测试,您的程序都会以同样快的速度运行。
【解决方案2】:

如果runtest() 受 CPU 限制——也就是说,不做任何可能阻塞 i/o 等的事情——那么启动 100 个线程没有多大意义,除非你有 100 个 CPU/核心! [编辑:我现在注意到runtest() 做了一些打印语句……文件 i/o 可能不会阻塞……所以不会释放 CPU。]

当前显示的代码在填充队列时保存互斥锁,因此在队列满之前不会启动任何操作。当队列填充完成 1000 次信号时,如果有任何到达 pthread_cond_wait(),那么希望它们都已启动 - 所以所有 100 将在互斥体上等待。

如当前所示,playQueue() 中的等待已中断。应该是这样的:

  pthread_mutex_wait(&queueLock) ;

  while (testQueue.empty)
    pthread_cond_wait(&queue_cv, &queueLock) ;

  if (testQueue.eof)
    val = NULL ;
  else
    val = testQueue.pop ;

  pthread_mutex_unlock(&queueLock) ;

但是,即使这一切都解决了,也不能保证您会看到性能上的改进,除非runtest() 做了大量的工作。 [编辑:我现在注意到它做了“大量的矩阵计算”,这听起来可能有很多工作。]

一个小建议,启动工作线程和填充队列可以重叠,例如通过启动一个工作线程来启动所有其他工作线程,或者启动一个线程来填充队列。

在不了解更多问题的情况下,如果可以在工作线程之间静态分配工作——例如,给第一个线程项目 0..9,第二个线程项目 10..19,依此类推——所以每个worker可以忽略所有其他的,减少同步操作的数量。

【讨论】:

    【解决方案3】:

    除了其他好的答案之外,您还说您的 runtest() 函数执行 I/O。

    因此,您很可能会受到 I/O 限制,在这种情况下,您的所有线程都必须像其他人一样排队等待清空缓冲区。

    【讨论】:

      猜你喜欢
      • 2016-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-26
      • 2019-04-23
      相关资源
      最近更新 更多