Parallel processing 并行合并排序性能
我试图直观地理解,如果我并行化合并排序,我可以加快多少速度 到目前为止,我的想法是: 如果N是数组中要排序的元素数,那么log(base 2)N是我需要的最高核心数。我认为这是因为mergesort中有2*log(基数2)N+1个级别。首先,通过反复将其除以2将其分解,然后反复合并两个已排序的数组,直到再次得到一个包含N个项的数组(现在已排序) 我想弄清楚这到底能在多大程度上提高性能。我认为,随着我们向算法的中间部分迈进,由于增加了内核而导致的性能提高将会增加,因为我们可以使用更多的内核。假设在未排序的数组中有16项。我只需要使用一个核心将其分解为两个8项数组,然后我可以使用两个核心将其分解为四个4项数组,以此类推 因此,对于每一级拆分,性能将提高两倍,然后对于每一级合并,性能将降低两倍。。。正当我走对了吗 还有,为什么我们不能从合并未排序数组中的前两项开始,然后合并下两项,依此类推。基本上去掉了算法的前半部分 想法Parallel processing 并行合并排序性能,parallel-processing,mergesort,Parallel Processing,Mergesort,我试图直观地理解,如果我并行化合并排序,我可以加快多少速度 到目前为止,我的想法是: 如果N是数组中要排序的元素数,那么log(base 2)N是我需要的最高核心数。我认为这是因为mergesort中有2*log(基数2)N+1个级别。首先,通过反复将其除以2将其分解,然后反复合并两个已排序的数组,直到再次得到一个包含N个项的数组(现在已排序) 我想弄清楚这到底能在多大程度上提高性能。我认为,随着我们向算法的中间部分迈进,由于增加了内核而导致的性能提高将会增加,因为我们可以使用更多的内核。假设在
我应该改在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);
}
}