关于C#:排序网络如何击败通用排序算法?

How does a sorting network beat generic sorting algorithms?

关于最快的固定长度6int数组排序,我不完全理解这种排序网络如何胜过插入排序之类的算法。

从这个问题来看,这里是完成排序所需的CPU周期数的比较:

Linux 32 bits, gcc 4.4.1, Intel Core 2 Quad Q8300, -O2

  • Insertion Sort (Daniel Stutzbach) : 1425
  • Sorting Networks (Daniel Stutzbach) : 1080

使用的代码如下:

Insertion Sort (Daniel Stutzbach)

1
2
3
4
5
6
7
8
9
static inline void sort6_insertion_sort_v2(int *d){
    int i, j;
    for (i = 1; i < 6; i++) {
            int tmp = d[i];
            for (j = i; j >= 1 && tmp < d[j-1]; j--)
                    d[j] = d[j-1];
            d[j] = tmp;
    }
}

Sorting Networks (Daniel Stutzbach)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static inline void sort6_sorting_network_v1(int * d){
#define SWAP(x,y) if (d[y] < d[x]) { int tmp = d[x]; d[x] = d[y]; d[y] = tmp; }
    SWAP(1, 2);
    SWAP(0, 2);
    SWAP(0, 1);
    SWAP(4, 5);
    SWAP(3, 5);
    SWAP(3, 4);
    SWAP(0, 3);
    SWAP(1, 4);
    SWAP(2, 5);
    SWAP(2, 4);
    SWAP(1, 3);
    SWAP(2, 3);
#undef SWAP
}

我知道,排序网络确实很适合并行排序,因为有些步骤独立于其他步骤。但这里我们不使用平行化。

我希望它更快,因为它的优点是事先知道元素的确切数量。插入排序在哪里以及为什么会进行不必要的比较?

Eddi1:

这是将这些代码与以下代码进行比较的输入集:

1
2
3
4
5
6
7
8
int d[6][6] = {\
    {1, 2, 3, 4, 5, 6},\
    {6, 5, 4, 3, 2, 1},\
    {100, 2, 300, 4, 500, 6},\
    {100, 2, 3, 4, 500, 6},\
    {1, 200, 3, 4, 5, 600},\
    {1, 1, 2, 1, 2, 1}\
};\


But here we are not using the parallelization.

现代的CPU可以计算出指令何时是独立的,并将并行执行它们。因此,即使只有一个线程,也可以利用排序网络的并行性。

Where exactly does insertion sort make unnecessary comparisons?

看到额外比较的最简单方法是手工做一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Insertion sort:
6 5 4 3 2 1
5 6 4 3 2 1
5 4 6 3 2 1
4 5 6 3 2 1
4 5 3 6 2 1
4 3 5 6 2 1
3 4 5 6 2 1
3 4 5 2 6 1
3 4 2 5 6 1
3 2 4 5 6 1
2 3 4 5 6 1
2 3 4 5 1 6
2 3 4 1 5 6
2 3 1 4 5 6
2 1 3 4 5 6
1 2 3 4 5 6

Sorting network:
6 5 4 3 2 1
6 4 5 3 2 1
5 4 6 3 2 1
4 5 6 3 2 1 # These three can execute in parallel with the first three
4 5 6 3 1 2 #
4 5 6 2 1 3 #
4 5 6 1 2 3
1 5 6 4 2 3
1 2 6 4 5 3
1 2 3 4 5 6
1 2 3 4 5 6


更好的问题是,为什么排序网络只比插入排序(通常是非常慢的排序)强约50%。答案是,当n很小时,big-o就不那么重要了。至于OP的问题,丹尼尔有最好的答案。


我相信并行算法和串行算法所做的"工作"量几乎是相同的。只有这样,由于工作得到了分配,您才能更快地获得输出。我认为,如果输入的大小足以证明使用并行算法是正确的,那么您得到的输出会更快得令人信服。

在插入排序的情况下,阵列在处理器之间的划分是这样的:它形成一条管道,填充管道需要一段时间,然后它将产生并行算法的好处。


我认为循环展开是导致排序网络算法更快结果的原因


我想你们所有的问题都是用丹尼尔·斯图茨巴赫的原版回答的:

The algorithm you posted is similar to
an insertion sort, but it looks like
you've minimized the number of swaps
at the cost of more comparisons.
Comparisons are far more expensive
than swaps, though, because branches
can cause the instruction pipeline to
stall.


理论上,如果编译器能够在插入排序中完全展开循环,代码可能是相同的。第一个循环可以轻松展开,而第二个循环则无法轻松展开。

也可能是这样,因为代码没有网络排序代码那么简单,编译器可以进行更少的优化。我认为插入排序中的依赖关系比网络排序中的依赖关系多,这在编译器试图优化代码时可能会有很大的不同(如果我错了,请纠正我)。