【发布时间】:2014-06-10 10:09:48
【问题描述】:
(我试图尽可能简化这一点,以找出我做错了什么。)
代码的想法是我有一个全局数组 *v(我希望使用这个数组不会减慢速度,线程不应该访问相同的值,因为它们都在不同的范围内工作)并且我尝试创建 2 个线程,每个线程分别通过调用带有相应参数的函数 merge_sort() 对前半部分和后半部分进行排序。
在线程运行时,我看到进程的 cpu 使用率达到 80-100%(在双核 cpu 上),而在没有线程运行时它仅保持在 50%,但运行时间非常接近。
这是(相关的)代码:
//这是2个排序函数,每个线程都会调用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中我只是创建了2个线程调用上面的函数
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<<"error\n;";
return err;
}
}
for (int i=0; i<2; i++) {
if (pthread_join(thread[i], &status) != 0) {
cerr<<"error\n;";
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 秒 4 个线程:4.692 秒
1000 万个值: 1 个线程:82.034 秒 2 个线程:46.189 秒 4 个线程:47.36 秒
【问题讨论】:
-
您的
merge仍然是连续的。在merge_sort和merge阶段花费的时间比例是多少? -
也只是为了确保您重新发明轮子:cplusplus.com/reference/algorithm/merge
-
您并没有真正节省多少,而是为您通过线程管理节省的费用付费。此外,您的合并可能会相当简单(实际上使用
std::inplace_merge会大大简化这一点)。为什么你甚至要启动两个线程?您可以轻松启动 one,然后将 current 线程用作“其他”。 -
@Adam 最终的合并调用需要 0.171 秒,我认为没有一种简单的方法可以检查它在这些函数中停留了多少时间。我知道合并是顺序的,但我认为使用 2 个内核而不是一个内核应该会加快很多速度。
-
@WhozCraig 我也使用 MPI 实现了这个程序,我试图保持相同的结构来比较它们。完整版的程序可以使用不同数量的线程。基本思想是这样的:如果未达到 N 个线程:|我创建新线程对当前范围的左半部分进行排序 |我创建新线程来排序当前范围的右半部分 |等他们两个说完|合并结果。现在,如果我想要 4 个线程,它们将从这 2 个创建新线程命令递归地创建
标签: c++ multithreading pthreads-win32