C++线程化进程运行速度慢于单线程问题
我试图在多个pthread上运行一个函数,以提高效率和运行时间。此函数执行大量矩阵计算和打印语句。但是,当我运行测试以查看性能改进时,单线程代码运行得更快 我的测试如下: -对于单线程:运行调用该函数的For循环1:1000 -对于multi-pthread:Spawn 100 pthreads,有一个包含1000个项目的队列和一个pthread_cond_wait,并让线程运行函数,直到队列为空 以下是我的pthreads单线程代码,它只是一个for循环: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
# 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是所有数学运算发生的地方,所以它应该能够自己运行。互斥锁唯一被锁定的时间是在它更新队列时。根据测试所花费的时间,这可能仍然是相同的问题。如果您的测试可以在几乎没有内存访问的情况下执行,那么测试可能仍然太快,无法产生任何影响。一个内核可以执行数百条算术指令,而另一个内核使用内存总线。在这种情况下,无论是否包含测试,您的程序都将以同样快的速度运行。