分拣网络如何击败通用排序算法?

时间:2010-10-10 16:17:56

标签: c algorithm sorting comparison sorting-network

在参考fastest sort of fixed length 6 int array时,我并不完全理解sorting network如何击败像insertion sort这样的算法。

表示该问题,这里是完成排序所需的CPU周期数的比较:

  

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

     
      
  • 插入排序(Daniel Stutzbach):1425
  •   
  • 排序网络(Daniel Stutzbach):1080
  •   

使用的代码如下:

  

插入排序(Daniel Stutzbach)

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;
    }
}
  

排序网络(Daniel Stutzbach)

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
}

据我所知,排序网络非常适合并行排序,因为有些步骤与其他步骤无关。但在这里我们没有使用并行化。

我希望它更快,因为它具有预先知道元素的确切数量的优点。 插入排序在何处以及为何进行了不必要的比较?

EDIT1:

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

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}\
};\

6 个答案:

答案 0 :(得分:19)

  

但是我们没有使用并行化。

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

  

插入排序究竟在哪里进行不必要的比较?

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

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

答案 1 :(得分:4)

更好的问题是为什么排序网络仅比插入排序(通常非常慢的排序)高出约50%。答案是当n很小时,大O不是那么重要。至于OP的问题,丹尼尔有最好的答案。

答案 2 :(得分:1)

我认为loop unwinding是导致排序网络算法更快结果的原因

答案 3 :(得分:1)

我相信在并行算法和串行算法中完成的“工作量”总是几乎相同。只有这样,因为工作分配,你会更快地得到输出。我认为如果输入的大小足以证明使用并行算法是合理的话,你会更快地获得输出。

如果在处理器之间插入排序分割,则它会形成一个管道,并且填充管道需要一些时间,然后它会产生并行算法的好处。

答案 4 :(得分:0)

理论上,如果编译器可以完全展开插入排序中的循环,则代码可能大致相同。第一个循环可以很容易地展开,而第二个循环不能轻松展开。

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

答案 5 :(得分:0)

我认为你们所有的问题都在Daniel Stutzbach回答原帖:

  

您发布的算法类似于   插入排序,但它看起来像   你已经减少掉期数量了   以更多的比较为代价。   比较要贵得多   但是,因为分支机构而不是互换   可以导致指令管道   失速。