C++线程化进程运行速度慢于单线程问题

C++线程化进程运行速度慢于单线程问题,c++,multithreading,performance,pthreads,C++,Multithreading,Performance,Pthreads,我试图在多个pthread上运行一个函数,以提高效率和运行时间。此函数执行大量矩阵计算和打印语句。但是,当我运行测试以查看性能改进时,单线程代码运行得更快 我的测试如下: -对于单线程:运行调用该函数的For循环1:1000 -对于multi-pthread:Spawn 100 pthreads,有一个包含1000个项目的队列和一个pthread_cond_wait,并让线程运行函数,直到队列为空 以下是我的pthreads单线程代码,它只是一个for循环: # include <iost

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

我的测试如下:

-对于单线程:运行调用该函数的For循环1:1000

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

以下是我的pthreads单线程代码,它只是一个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++,必须下载一个库来使用pthult.< /p> ,首先编写代码时,只有一个线程在任何时候都运行你的互斥锁,直到线程工作的整个时间。因此,您最多可以期望您的代码与单线程版本一样快


此外,所有线程每次都读取和写入相同的内存。通过这种方式,您可以强制CPU内核同步它们的缓存,这意味着总线上的负载实际上比单个线程造成的负载更多。由于您没有做任何计算上昂贵的工作,内存带宽很可能是您的实际瓶颈,因此缓存同步增加的总线负载会降低程序的速度。请查看以获取更多信息。

首先,您的代码的编写方式是,在线程执行工作的整个过程中,当您的互斥锁被锁定时,只有一个线程会运行。因此,您最多可以期望您的代码与单线程版本一样快

此外,所有线程每次都读取和写入相同的内存。通过这种方式,您可以强制CPU内核同步它们的缓存,这意味着总线上的负载实际上比单个线程造成的负载更多。由于您没有做任何计算上昂贵的工作,内存带宽很可能是您的实际瓶颈,因此缓存同步增加的总线负载会降低程序的速度。查看以获取更多信息。

如果runtest受CPU限制,即不执行任何可能阻塞i/o或类似操作的操作,那么启动100个线程没有多大意义,除非您有100个CPU/内核![编辑:我现在注意到runtest执行一些打印语句…文件I/o可能不会阻塞…因此不会释放CPU。]

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

如当前所示,正在播放的队列已中断。它应该是大致如下的东西:

  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等等-那么每个工作线程都可以忽略所有其他线程项,从而减少同步操作的数量。

如果runtest是CPU限制的-也就是说,不做任何可能阻塞i/o或类似操作的事情——那么开始100个线程就没有什么意义了,除非您有100个CPU/内核![编辑:我现在注意到runtest执行一些打印语句…文件I/o可能不会阻塞…因此不会释放CPU。]

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

如当前所示,正在播放的队列已中断。它应该是大致如下的东西:

  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等等-那么每个工作线程都可以忽略所有其他线程项,从而减少同步操作的数量。

除了其他好的答案之外,你说过你的runtest函数做I/O


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

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


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

您的playQueue被声明为返回指针,但代码不返回任何值。那是未定义的行为。另外,您的代码中有一个*/而没有匹配/*另外,您有100个cpu内核吗?否则,即使您的程序是并行的,大多数线程也会在队列中等待,浪费同步开销。playQueue实际上不做任何工作。您正在测量创建和删除线程的纯开销。@IgorTandetnik很抱歉我抄错了,但我编辑了这个问题。线程应该执行函数runtest,它包含了所有的数学知识。在担心让程序变快之前,您需要将其更正。未定义的行为有两个来源:1个playQueue调用而不首先锁定互斥体,2个main销毁互斥体和条件变量而不等待线程完成。您不应该运行很多线程。100美元几乎总是过高。尝试运行比内核多一点的线程,即在Linux上运行grep processor/proc/cpuinfo | wc-l。。。。因此,请尝试使用5、10、15和20个线程。您的playQueue声明为返回指针,但代码不返回任何值。那是未定义的行为。另外,您的代码中有一个*/而没有匹配/*另外,您有100个cpu内核吗?否则,即使您的程序是并行的,大多数线程也会在队列中等待,浪费同步开销。playQueue实际上不做任何工作。您正在测量创建和删除线程的纯开销。@IgorTandetnik很抱歉我抄错了,但我编辑了这个问题。线程应该执行函数runtest,它包含了所有的数学知识。在担心让程序变快之前,您需要将其更正。未定义的行为有两个来源:1个playQueue调用而不首先锁定互斥体,2个main销毁互斥体和条件变量而不等待线程完成。您不应该运行很多线程。100美元几乎总是过高。尝试运行比内核多一点的线程,即在Linux上运行grep processor/proc/cpuinfo | wc-l。。。。所以试着用5、10、15和20个线程。我对这个问题进行了编辑,因为我忘记了一个主要部分,那就是它调用的函数。所以函数runtest是所有数学运算发生的地方,所以它应该能够自己运行。互斥锁唯一被锁定的时间是在它更新队列时。根据测试所花费的时间,这可能仍然是相同的问题。如果您的测试可以在几乎没有内存访问的情况下执行,那么测试可能仍然太快,无法产生任何影响。一个内核可以执行数百条算术指令,而另一个内核使用内存总线。在这种情况下,无论是否包含测试,您的程序都会以同样快的速度运行。我对问题进行了编辑,因为我忘记了一个主要部分,那就是它调用的函数。所以函数runtest是所有数学运算发生的地方,所以它应该能够自己运行。互斥锁唯一被锁定的时间是在它更新队列时。根据测试所花费的时间,这可能仍然是相同的问题。如果您的测试可以在几乎没有内存访问的情况下执行,那么测试可能仍然太快,无法产生任何影响。一个内核可以执行数百条算术指令,而另一个内核使用内存总线。在这种情况下,无论是否包含测试,您的程序都将以同样快的速度运行。