合并排序pThread实现的时间与单线程相同
Mergesort pThread implementation taking same time as single-threaded
(我已经尽量简化这一点,我可以找出我做错了什么)
代码的想法是,我有一个全局数组*v(我希望使用这个数组不会减慢速度,线程不应该访问相同的值,因为它们都在不同的范围内工作),我尝试创建2个线程,每个线程排序前半部分,分别通过调用函数merge_sort()与各自的参数。
在线程运行时,我看到进程的cpu使用率达到80-100%(双核cpu),而在无线程运行时,它只保持在50%,但运行时间非常接近。
这是(相关)代码:
//这是两个排序函数,每个线程将调用merge_sort(..)。这是个问题吗?两个线程调用相同的(正常)函数?
void merge (int *v, int start, int middle, int end) {
//dynamically creates 2 new arrays for the v[start..middle] and v[middle+1..end]
//copies the original values into the 2 halves
//then sorts them back into the v array
}
void merge_sort (int *v, int start, int end) {
//recursively calls merge_sort(start, (start+end)/2) and merge_sort((start+end)/2+1, end) to sort them
//calls merge(start, middle, end)
}
//这里我希望每个线程都被创建,并在其特定范围内调用merge_sort(这是原始代码的简化版本,以便更容易找到错误)
void* mergesort_t2(void * arg) {
t_data* th_info = (t_data*)arg;
merge_sort(v, th_info->a, th_info->b);
return (void*)0;
}
//在main中,我简单地创建了两个线程,调用上面的函数
int main (int argc, char* argv[])
{
//some stuff
//getting the clock to calculate run time
clock_t t_inceput, t_sfarsit;
t_inceput = clock();
//ignore crt_depth for this example (in the full code i'm recursively creating new threads and i need this to know when to stop)
//the a and b are the range of values the created thread will have to sort
pthread_t thread[2];
t_data next_info[2];
next_info[0].crt_depth = 1;
next_info[0].a = 0;
next_info[0].b = n/2;
next_info[1].crt_depth = 1;
next_info[1].a = n/2+1;
next_info[1].b = n-1;
for (int i=0; i<2; i++) {
if (pthread_create (&thread[i], NULL, &mergesort_t2, &next_info[i]) != 0) {
cerr<<"errorn;";
return err;
}
}
for (int i=0; i<2; i++) {
if (pthread_join(thread[i], &status) != 0) {
cerr<<"errorn;";
return err;
}
}
//now i merge the 2 sorted halves
merge(v, 0, n/2, n-1);
//calculate end time
t_sfarsit = clock();
cout<<"Sort time (s): "<<double(t_sfarsit - t_inceput)/CLOCKS_PER_SEC<<endl;
delete [] v;
}
输出(100万个值):
Sort time (s): 1.294
直接调用merge_sort的输出,没有线程:
Sort time (s): 1.388
输出(1000万个值):
Sort time (s): 12.75
直接调用merge_sort的输出,没有线程:
Sort time (s): 13.838
解决方案:
我也要感谢WhozCraig和Adam,因为他们从一开始就暗示了这一点。
我使用了inplace_merge(..)
函数而不是我自己的函数,程序运行时间现在应该是这样的。
这是我的初始合并函数(不确定是否初始,我可能已经修改了几次,数组索引现在可能是错误的,我在[a,b]和[a,b)之间来回,这只是最后一个注释版本):
void merge (int *v, int a, int m, int c) { //sorts v[a,m] - v[m+1,c] in v[a,c]
//create the 2 new arrays
int *st = new int[m-a+1];
int *dr = new int[c-m+1];
//copy the values
for (int i1 = 0; i1 <= m-a; i1++)
st[i1] = v[a+i1];
for (int i2 = 0; i2 <= c-(m+1); i2++)
dr[i2] = v[m+1+i2];
//merge them back together in sorted order
int is=0, id=0;
for (int i=0; i<=c-a; i++) {
if (id+m+1 > c || (a+is <= m && st[is] <= dr[id])) {
v[a+i] = st[is];
is++;
}
else {
v[a+i] = dr[id];
id++;
}
}
delete st, dr;
}
所有这些都被替换为:
inplace_merge(v+a, v+m, v+c);
编辑,有时在我的3ghz双核cpu:
100万个值:1线程:7.236秒2螺纹:4.622 s4个线程:4.692 s
1000万个值:1螺纹:82.034秒2个线程:46.189秒4个线程:47.36 s
有一件事打动了我:"动态创建2个新数组[…]"。由于两个线程都需要从系统获得内存,因此它们需要为此获得一个锁,这很可能成为瓶颈。特别是微观数组分配的想法听起来非常低效。有人建议使用不需要任何额外存储的就地排序,这对性能来说要好得多。
另一件事是经常被遗忘的任何大o复杂度测量的开头半句:"有一个n0,因此对于所有n>n0…"。换句话说,也许你还没有达到第0个?我最近看了一个视频(希望有人会记得),有人试图为一些算法确定这个极限,他们的结果是这些极限惊人的高
注意:由于OP使用Windows,我下面的答案(错误地假设Linux)可能不适用。我把它留下是为了那些可能会发现这些信息有用的人。
在Linux上,
clock()
是一个错误的测量时间的接口:它测量程序使用的CPU时间(参见http://linux.die.net/man/3/clock),在多线程的情况下,它是所有线程的CPU时间之和。您需要测量经过的时间或时钟时间。C:在多线程程序中使用clock()来测量时间,这也告诉我们可以使用什么API来代替clock()
。
在您尝试比较的基于MPI的实现中,使用了两个不同的进程(这就是MPI通常支持并发性的方式),并且不包括第二个进程的CPU时间-因此CPU时间接近时钟时间。然而,即使在串行程序中,使用CPU时间(以及clock()
)进行性能测量仍然是错误的;原因之一是,如果一个程序等待网络事件或来自另一个MPI进程的消息,它仍然会花费时间——但不是CPU时间。
Update:在Microsoft的C运行时库实现中,clock()
返回时钟时间,因此可以用于您的目的。如果你使用微软的工具链或其他东西,如Cygwin或MinGW,这是不清楚的。
- 在C/C++中执行"_dl_init"(程序初始化)是单线程的吗
- 特定对象文件的单线程生成
- 如何在类中同时运行某些内容(在单线程平台中)?
- GDB 8.1 无法在单线程简单程序中跟踪 std::string 变量的值
- 双线程应用比单线程应用慢 C++ (VC++ 2010 Express).如何解决?
- OpenMP:共享同一算法的单线程和多线程实现
- std::原子布尔值或普通全局布尔值在单线程中很好吗?
- 重写多线程事件驱动的C 程序以使用单线程Boost :: Asio
- C++多线程操作比单线程慢
- 多线程和单线程代码维护
- 线程池的计时测试:单线程 vs 回调 tp vs 未来 tp
- 在 Linux 中从单独的单线程进程生成多线程进程时出现问题
- C++多线程性能比单线程代码慢
- 即使在单线程程序中,共享指针是否在引用计数中使用原子操作
- 从单线程到多线程图像处理
- 当发送多个同时请求时,单线程异步系统中Beast Boost异步HTTP客户端的行为
- 多线程与单线程快速排序花费相同的时间
- 单线程和多线程代码需要同时花费时间
- 合并排序pThread实现的时间与单线程相同
- 多线程程序比单线程程序执行时间更长