Parallel processing 并行合并排序性能

Parallel processing 并行合并排序性能,parallel-processing,mergesort,Parallel Processing,Mergesort,我试图直观地理解,如果我并行化合并排序,我可以加快多少速度 到目前为止,我的想法是: 如果N是数组中要排序的元素数,那么log(base 2)N是我需要的最高核心数。我认为这是因为mergesort中有2*log(基数2)N+1个级别。首先,通过反复将其除以2将其分解,然后反复合并两个已排序的数组,直到再次得到一个包含N个项的数组(现在已排序) 我想弄清楚这到底能在多大程度上提高性能。我认为,随着我们向算法的中间部分迈进,由于增加了内核而导致的性能提高将会增加,因为我们可以使用更多的内核。假设在

我试图直观地理解,如果我并行化合并排序,我可以加快多少速度

到目前为止,我的想法是:

如果N是数组中要排序的元素数,那么log(base 2)N是我需要的最高核心数。我认为这是因为mergesort中有2*log(基数2)N+1个级别。首先,通过反复将其除以2将其分解,然后反复合并两个已排序的数组,直到再次得到一个包含N个项的数组(现在已排序)

我想弄清楚这到底能在多大程度上提高性能。我认为,随着我们向算法的中间部分迈进,由于增加了内核而导致的性能提高将会增加,因为我们可以使用更多的内核。假设在未排序的数组中有16项。我只需要使用一个核心将其分解为两个8项数组,然后我可以使用两个核心将其分解为四个4项数组,以此类推

因此,对于每一级拆分,性能将提高两倍,然后对于每一级合并,性能将降低两倍。。。正当我走对了吗

还有,为什么我们不能从合并未排序数组中的前两项开始,然后合并下两项,依此类推。基本上去掉了算法的前半部分

想法


我应该改在math.stackexchange.com上问这个问题吗?对不起,如果是这样。。。我真的不知道

如果您想通过并行化提高MergeSort的性能,您应该并行化拆分(合并结果之前所做的部分)。我假设您有多个CPU节点

拆分: 让当前CPU节点保留阵列的一半,并将另一半交给另一个CPU节点。继续重复这个过程。并行性将随着树的深入而增加(如您所述)

基本情况: 当数据是一项时,当前CPU节点将其发送回其父节点。 父节点将等待子节点传递数据,然后再进行任何合并

合并: 一旦从节点的子节点接收到数据,节点(该子节点的父节点)就可以开始将接收到的数据与其自己的数据合并。合并完成后,它将其传递给其父节点,依此类推。因为每个节点都是一个单独的CPU,所以在较低级别的合并是并行进行的。这种平行性随着我们爬上树而减少。(就像你提到的)

这将加快合并排序


然而,wikipedia上的这篇文章显示,通过并行化和专门化,您可以将合并步骤加速到O(1)(以及在数据大小时插入插入排序)如果您想通过并行化提高MergeSort的性能,您应该并行化拆分(合并结果之前所做的部分)。我假设您有多个CPU节点

拆分: 让当前CPU节点保留阵列的一半,并将另一半交给另一个CPU节点。继续重复此过程。随着深入到树中(如您所述),并行性将增加

基本情况: 当数据是一项时,当前CPU节点将其发送回其父节点。 父节点将等待子节点传递数据,然后再进行任何合并

合并: 一旦从节点的子节点接收到数据,节点(该子节点的父节点)就可以开始将接收到的数据与其自己的数据进行合并。合并完成后,它将数据传递给其父节点,依此类推。由于每个节点都是一个单独的CPU,在较低级别上的合并是并行进行的。这种并行性随着树的上升而降低。(就像你提到的)

这将加快合并排序

但是,wikipedia上的这篇文章显示,通过并行化和专门化合并步骤(以及在数据大小1时引入插入排序),可以将合并步骤加速到O(1),并让每个处理器对数组的n/p进行串行排序

2) 对于i=1到log_2(p) 必须使用2^i处理器合并两个阵列。 在O(log 2^i)时间内,使用一个处理器进行二进制搜索,将数组最大部分(我们将其分成两部分)的中点搜索到另一个数组,以找到匹配的位置,并在那里形成另一个分区

例如:

A=123456789

B=123456789

最大的部分是12345,中点是3。使用二进制搜索查找此3在另一个数组中的位置并将其拆分。新数组:

A=12345 6789 B=123456789

您可以使用优先级队列跟踪哪个数组部分最大

将A和B数组拆分为O(p)部分后,可以对每个小数据块并行进行串行合并。要获得每个配对输出的偏移量,可以先进行并行前缀和

O(n/p log n/p)//串行排序,如果可以进行基数排序,则仅使用O(n/p)

O(log(p)*(log(p)+(n/p))=O(log(p)^2+log(p)(n/p))//并行合并

1)让每个处理器对数组的n/p进行串行排序

2) 对于i=1到log_2(p) 必须使用2^i处理器合并两个阵列。 在O(log 2^i)时间内,使用一个处理器进行二进制搜索,将数组最大部分(我们将其分成两部分)的中点搜索到另一个数组,以找到匹配的位置,并在那里形成另一个分区

例如:

A=123456789

B=123456789

最大截面为12345,中点为3。使用二进制搜索查找此3在另一个数组中的位置并拆分它。新阵列:

A=12345 6789 B=123456789

您可以使用优先级队列跟踪哪个数组部分最大

将A和B数组拆分为O(p)部分后,可以对每个小数据块并行进行串行合并。获取每个配对的输出位置的偏移量
protected static void ASC(int[]a, int left, int right, int div)
{
    int len = 1 + right - left;
    if (len < 27)
    {
        // insertion sort for small array
        int P1 = left + 1;
        int P2 = left;
        while ( P1 <= right )
        {
            div = a[P1];
            while(( P2 >= left )&&( a[P2] > div ))
            {
                a[P2 + 1] = a[P2];
                P2--;
            }
            a[P2 + 1] = div;
            P2 = P1;
            P1++;
        }
        return;
    }
    int third = len / div;
    // "medians"
    int P1 = left + third;
    int P2 = right - third;
    if (P1 <= left)
    {
        P1 = left + 1;
    }
    if (P2 >= right)
    {
        P2 = right - 1;
    }
    int temp;
    if (a[P1] < a[P2])
    {
        temp = a[P1]; a[P1] = a[left]; a[left] = temp;
        temp = a[P2]; a[P2] = a[right]; a[right] = temp;
    }
    else
    {
        temp = a[P1];  a[P1] = a[right];  a[right] = temp;
        temp = a[P2];  a[P2] = a[left];  a[left] = temp;
    }
    // pivots
    int pivot1 = a[left];
    int pivot2 = a[right];
    // pointers
    int less = left + 1;
    int great = right - 1;
    // sorting
    for (int k = less; k <= great; k++)
    {
        if (a[k] < pivot1)
        {
            temp = a[k];  a[k] = a[less];  a[less] = temp;
            less++;
        }
        else if (a[k] > pivot2)
        {
            while (k < great && a[great] > pivot2)
            {
                great--;
            }
            temp = a[k];  a[k] = a[great];  a[great] = temp;
            great--;
            if (a[k] < pivot1)
            {
                temp = a[k];  a[k] = a[less];  a[less] = temp;
                less++;
            }
        }
    }
    int dist = great - less;
    if (dist < 13)
    {
        div++;
    }
    temp = a[less-1];  a[less-1] = a[left];  a[left] = temp;
    temp = a[great+1];  a[great+1] = a[right];  a[right] = temp;
    // subarrays
    ASC(a, left, less - 2, div);
    ASC(a, great + 2, right, div);
    // equal elements
    if (dist > len - 13 && pivot1 != pivot2)
    {
        for (int k = less; k <= great; k++)
        {
            if (a[k] == pivot1)
            {
                temp = a[k];  a[k] = a[less];  a[less] = temp;
                less++;
            }
            else if (a[k] == pivot2)
            {
                temp = a[k];  a[k] = a[great];  a[great] = temp;
                great--;
                if (a[k] == pivot1)
                {
                    temp = a[k];  a[k] = a[less];  a[less] = temp;
                    less++;
                }
            }
        }
    }
    // subarray
    if (pivot1 < pivot2)
    {
        ASC(a, less, great, div);
    }
}

protected static void DSC(int[]a, int left, int right, int div)
{
    int len = 1 + right - left;
    if (len < 27)
    {
        // insertion sort for large array
        int P1 = left + 1;
        int P2 = left;
        while ( P1 <= right )
        {
            div = a[P1];
            while(( P2 >= left )&&( a[P2] < div ))
            {
                a[P2 + 1] = a[P2];
                P2--;
            }
            a[P2 + 1] = div;
            P2 = P1;
            P1++;
        }
        return;
    }
    int third = len / div;
    // "medians"
    int P1 = left + third;
    int P2 = right - third;
    if (P1 >= left)
    {
        P1 = left + 1;
    }
    if (P2 <= right)
    {
        P2 = right - 1;
    }
    int temp;
    if (a[P1] > a[P2])
    {
        temp = a[P1]; a[P1] = a[left]; a[left] = temp;
        temp = a[P2]; a[P2] = a[right]; a[right] = temp;
    }
    else
    {
        temp = a[P1];  a[P1] = a[right];  a[right] = temp;
        temp = a[P2];  a[P2] = a[left];  a[left] = temp;
    }
    // pivots
    int pivot1 = a[left];
    int pivot2 = a[right];
    // pointers
    int less = left + 1;
    int great = right - 1;
    // sorting
    for (int k = less; k <= great; k++)
    {
        if (a[k] > pivot1)
        {
            temp = a[k];  a[k] = a[less];  a[less] = temp;
            less++;
        }
        else if (a[k] < pivot2)
        {
            while (k < great && a[great] < pivot2)
            {
                great--;
            }
            temp = a[k];  a[k] = a[great];  a[great] = temp;
            great--;
            if (a[k] > pivot1)
            {
                temp = a[k];  a[k] = a[less];  a[less] = temp;
                less++;
            }
        }
    }
    int dist = great - less;
    if (dist < 13)
    {
        div++;
    }
    temp = a[less-1];  a[less-1] = a[left];  a[left] = temp;
    temp = a[great+1];  a[great+1] = a[right];  a[right] = temp;
    // subarrays
    DSC(a, left, less - 2, div);
    DSC(a, great + 2, right, div);
    // equal elements
    if (dist > len - 13 && pivot1 != pivot2)
    {
        for (int k = less; k <= great; k++)
        {
            if (a[k] == pivot1)
            {
                temp = a[k];  a[k] = a[less];  a[less] = temp;
                less++;
            }
            else if (a[k] == pivot2)
            {
                temp = a[k];  a[k] = a[great];  a[great] = temp;
                great--;
                if (a[k] == pivot1)
                {
                    temp = a[k];  a[k] = a[less];  a[less] = temp;
                    less++;
                }
            }
        }
    }
    // subarray
    if (pivot1 > pivot2)
    {
        DSC(a, less, great, div);
    }
}