【问题标题】:How does a sorting network beat generic sorting algorithms?排序网络如何击败通用排序算法?
【发布时间】:2010-10-10 16:17:56
【问题描述】:

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

【问题讨论】:

  • 这里输入数组的顺序是随机的吗?还是您使用的是降序数组?

标签: c algorithm sorting comparison sorting-network


【解决方案1】:

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

现代 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

【讨论】:

  • @Daniel:好的,由于这些路径完全不同,我们无法直接比较它们。当然,排序网络允许我们以较少的比较次数进行排序。换一种方式来说明我的问题,是什么阻止我们优化插入排序以将这个交换序列用于任意数量的输入?
  • Lazer:恐怕我听不懂。当你说“这个交换序列”时,你指的是哪个序列?另外,您是说“优化插入排序”还是指排序网络?
  • @Daniel:对不起,不清楚。换句话说,如果排序网络更高效,我们为什么还要使用插入排序?
  • @Lazer:啊,这更有意义。 :-) 感谢您的澄清!排序网络的问题在于它们只适用于固定的 n。此外,它们仅在 n 较小时才实用,因为您必须手动写出所有比较和交换,并且它们的数量为 O(n log n)。它们速度快的部分原因是代码被写出来并且没有循环,所以速度是有限制的。
  • @Lazer:是的,我就是这个意思。 :-) 如果一个算法使用变量 n,它需要在某个地方有某种循环。排序网络没有循环。您可以编写一个程序来生成交换然后执行它们,但是生成交换所消耗的时间比使用排序网络节省的时间要多。最接近的方法是使用 MergeSort 或 QuickSort 等递归算法,并使用排序网络作为基本情况。
【解决方案2】:

更好的问题是为什么排序网络只比插入排序(通常是非常慢的排序)高出约 50%。答案是当n 很小时,big-O 并不那么重要。至于OP的问题,Daniel给出了最好的答案。

【讨论】:

  • 还是很重要的!当你有 1000000 个微小的种类时,即使是很小的差异也会产生变化。
  • @DenRoman:当你有 1000000 个小排序时,Big-O 并不重要。相反,在这种情况下,常数因素才是最重要的。
【解决方案3】:

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

【讨论】:

    【解决方案4】:

    我相信并行算法和串行算法完成的“工作量”总是几乎相同。只有这样,由于工作得到分配,您才能更快地获得输出。如果输入的大小足以证明使用并行算法的合理性,我认为您会更快地获得令人信服的输出。

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

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        【解决方案6】:

        我想你们所有的问题都在Daniel Stutzbach回复原帖中得到了回答:

        您发布的算法类似于 插入排序,但看起来像 您已将交换次数降至最低 以更多的比较为代价。 比较要贵得多 但是,比交换,因为分支 会导致指令流水线 摊位。

        【讨论】:

        • 你不能做出这样的概括。如果您的数据对象很大,但提取和比较密钥的速度很快,那么比较比交换便宜得多。我猜想交换更便宜的唯一时间是当您的数据元素是简单类型时。
        猜你喜欢
        • 2018-10-07
        • 1970-01-01
        • 2013-12-01
        • 2017-06-30
        • 2018-12-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-08
        • 1970-01-01
        相关资源
        最近更新 更多