Mergesort pThread实现与单线程同时进行

时间:2014-06-10 10:09:48

标签: c++ multithreading pthreads-win32

(我尽可能地简化了这一点,以便找出我做错事的地方。)

代码的ideea是我有一个全局数组* v(我希望使用这个数组不会减慢速度,线程永远不会访问相同的值,因为它们都在不同的范围内工作)和我尝试创建2个线程,每个线程分别对前半部分进行排序,分别通过使用相应参数调用函数merge_sort()来分别对后半部分进行排序。

在线程运行中,我看到进程达到80-100%cpu使用率(在双核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 s 4个主题:4.692 s

1000万价值观: 1个主题:82.034 s 2个主题:46.189 s 4个主题:47.36 s

2 个答案:

答案 0 :(得分:0)

注意:由于OP使用Windows,我的答案(错误地假设Linux)可能不适用。为了那些可能会发现信息有用的人,我把它留下了。


clock()是一个用于测量Linux时间的错误接口:它测量程序使用的CPU时间(参见http://linux.die.net/man/3/clock),在多线程的情况下,它是所有线程的CPU时间总和。您需要测量经过时间或挂钟时间。请参阅此SO问题中的更多详细信息:C: using clock() to measure time in multi-threaded programs,其中还说明可以使用哪种API代替clock()

在您尝试与之比较的基于MPI的实现中,使用了两个不同的进程(MPI通常如何启用并发),并且不包括第二个进程的CPU时间 - 因此CPU时间接近挂钟时间。然而,即使在串行程序中,使用CPU时间(以及clock())进行性能测量仍然是错误的;出于一个原因,如果程序等待例如一个网络事件或来自另一个MPI进程的消息,它仍然花费时间 - 但不花费CPU时间。

更新:在Microsoft的C运行时库clock() returns wall-clock time的实现中,可以用于您的目的。如果你使用微软的工具链或其他东西,比如Cygwin或MinGW,目前还不清楚。

答案 1 :(得分:0)

有一件事让我印象深刻:&#34;动态创建2个新数组[...]&#34;。由于两个线程都需要来自系统的内存,因此需要为此获取锁定,这很可能是您的瓶颈。特别是进行微观阵列分配的想法听起来非常低效。有人提出了一个不需要任何额外存储的就地排序,这对性能要好得多。

另一件事是任何大O复杂度测量经常被遗忘的开始半句:&#34;有一个n0,所以对于所有n&gt; n0 ...&#34;。换句话说,也许你还没达到n0?我最近看到一个视频(希望其他人会记住它),有些人试图确定某些算法的限制,他们的结果是这些限制非常高