C++ Mergesort pThread实现占用与单线程相同的时间

C++ Mergesort pThread实现占用与单线程相同的时间,c++,multithreading,pthreads-win32,C++,Multithreading,Pthreads Win32,(我已经尽可能地简化了这一点,以找出我做错了什么。) 代码的思想是我有一个全局数组*v(我希望使用这个数组不会减慢速度,线程永远不会访问相同的值,因为它们都在不同的范围内工作),我尝试创建两个线程,每个线程对前半部分排序,通过调用函数merge_sort()分别对后半部分排序使用相应的参数 在线程运行时,我看到进程将达到80-100%的cpu使用率(在双核cpu上),而在无线程运行时,它仅保持在50%,但运行时间非常接近 这是(相关)代码: //这是两个排序函数,每个线程将调用merge_so

(我已经尽可能地简化了这一点,以找出我做错了什么。)

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

在线程运行时,我看到进程将达到80-100%的cpu使用率(在双核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(这是原始代码的简化版本,以便更容易发现错误)

//大体上,我只是创建了两个线程来调用上面的函数

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;
}
直接调用merge_sort的输出,无线程:

Sort time (s): 1.388
Sort time (s): 13.838

产出(1000万美元):

直接调用merge_sort的输出,无线程:

Sort time (s): 1.388
Sort time (s): 13.838

解决方案:

我也要感谢WhozCraig和Adam,因为他们从一开始就暗示了这一点

我使用了
inplace\u merge(..)
函数,而不是我自己的函数,程序运行时间与现在一样

这是我的初始合并函数(不确定初始值是否正确,我可能已经修改了几次,因为现在数组索引可能是错误的,我在[a,b]和[a,b]之间来回切换,这只是最后一个注释掉的版本):


编辑,有时在我的3ghz双核cpu上:

100万价值: 1螺纹:7.236 s 2螺纹:4.622 s 4螺纹:4.692 s

1000万美元: 1螺纹:82.034 s 2螺纹:46.189 s
4线程:47.36 s

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


clock()
在Linux上测量时间是一个错误的接口:它测量程序使用的CPU时间(请参阅),在多个线程的情况下,它是所有线程的CPU时间之和。您需要测量已用时间或Wallcock时间。请参阅此SO问题中的更多详细信息:,它还告诉您可以使用什么API来代替
clock()

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


更新:在Microsoft的C运行时库实现中,出于您的目的使用so是可以的。但不清楚您是否使用Microsoft的工具链或其他东西,如Cygwin或MinGW。

有一件事让我印象深刻:“动态创建2个新阵列[…]”。由于两个线程都需要系统内存,因此它们需要为此获取一个锁,这很可能是您的瓶颈。特别是执行微观阵列分配的想法听起来效率极低。有人建议使用不需要任何额外存储的就地排序,这对性能有很大好处


另一件事是对于任何big-O复杂性度量,经常被遗忘的开始半句话:“有一个n0,所以对于所有的n>n0…”。换句话说,也许你还没有达到n0?我最近看了一个视频(希望其他人会记得它)有些人试图确定某些算法的这个极限,他们的结果是这些极限出乎意料地高

你的
合并
仍然是顺序的。在
合并_排序
合并
阶段花费的时间比例是多少?也就是为了确保你要重新发明轮子:你并没有真正节省太多钱,也没有为你用线程管理节省的钱买单。此外,你的合并可能会简单得多(事实上,使用会大大简化这一点)。你为什么还要启动两个线程呢?你可以很容易地启动一个线程,然后将当前线程用作“另一个”线程.@Adam最终的合并调用需要0.171秒,我认为没有一种简单的方法来检查它在这些函数中停留的时间。我知道合并是顺序的,但我认为使用两个内核而不是一个内核应该会加快很多。@WhozCraig我也使用MPI实现了这个程序,我正试图保持相同的结构以使e它们之间的比较。完整版本的程序可以使用不同数量的线程。基本思想是:如果没有达到N个线程:|我创建新线程对当前范围的左半部分进行排序|我创建新线程对当前范围的右半部分进行排序|等待两个线程完成|合并结果。现在,如果我想要4个线程,它们将从2条create new thread命令递归创建,并使用timeGetTime()对其进行刺激相反,我给出了相同的数字。而且我自己也尝试过计时,看起来这并不是问题所在,我认为我的代码不可能同时运行线程。哦,你在Windows上运行吗?我被pthreads的使用误导了,并假设为Linux。是的,很抱歉没有提到。我正要发布这个,我已经修改了我的程序以在Linux中使用将_merge替换为我的merge实现,现在数字看起来应该是这样的。
Sort time (s): 13.838
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);