当前位置: 首页 > 知识库问答 >
问题:

为什么处理未排序数组的速度与处理排序数组的速度相同?

丁沛
2023-03-14

我发现了这个流行的9岁左右的问题,并决定重新检查它的结果。

所以,我有AMD Ryzen 9 595 0x、Clang++10和Linux,我从问题中复制粘贴了代码,下面是我得到的:

分类-0.549702秒:

~/d/so_sorting_faster$ cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
    std::sort(data, data + arraySize);
0.549702
sum = 314931600000

未排序-0.546554s:

~/d/so_sorting_faster $ cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
    // std::sort(data, data + arraySize);
0.546554
sum = 314931600000

我很确定的事实是,未经排序的版本被证明是快了3ms,只是噪音,但它似乎不再慢了。

那么,CPU的架构发生了什么变化(以至于不再慢一个数量级)?

以下是多次运行的结果:

Unsorted: 0.543557 0.551147 0.541722 0.555599
Sorted:   0.542587 0.559719 0.53938  0.557909

以防万一,这里是我的main.cpp:

#include <algorithm>
#include <ctime>
#include <iostream>

int main()
{
    // Generate data
    const unsigned arraySize = 32768;
    int data[arraySize];

    for (unsigned c = 0; c < arraySize; ++c)
        data[c] = std::rand() % 256;

    // !!! With this, the next loop runs faster.
    // std::sort(data, data + arraySize);

    // Test
    clock_t start = clock();
    long long sum = 0;

    for (unsigned i = 0; i < 100000; ++i)
    {
        // Primary loop
        for (unsigned c = 0; c < arraySize; ++c)
        {
            if (data[c] >= 128)
                sum += data[c];
        }
    }

    double elapsedTime = static_cast<double>(clock() - start) / CLOCKS_PER_SEC;

    std::cout << elapsedTime << std::endl;
    std::cout << "sum = " << sum << std::endl;
    return 0;
}

更新

具有较大数量的元素(627680):

Unsorted
cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
    // std::sort(data, data + arraySize);
10.3814

Sorted:
cat main.cpp | grep "std::sort" && clang++ -O3 main.cpp && ./a.out
    std::sort(data, data + arraySize);
10.6885

我认为问题仍然相关--几乎没有区别。

共有2个答案

杜禄
2023-03-14

可能是在对数据进行排序之后,它们(其中一些)仍然在缓存中,因此在下面的代码中可以更快地访问它们。

韦阳辉
2023-03-14

您链接的问题中的几个答案谈到将代码改写为无分支,从而避免任何分支预测问题。这就是您更新的编译器正在做的事情。

具体地说,clang++10和-o3对内部循环进行矢量化。请参阅godbolt上的代码,程序集的第36-67行。代码有点复杂,但有一点您绝对看不到,那就是data[c]>=128测试上的任何条件分支。相反,它使用向量比较指令(pcmpgtd),其输出是一个掩码,其中1表示匹配元素,0表示不匹配元素。带有此掩码的后续pand将不匹配的元素替换为0,这样它们在无条件添加到和中时就不会贡献任何东西。

C++的大致等价物是

sum += data[c] & -(data[c] >= 128);

代码实际上保留了两个运行的64位sums,用于数组的偶数和奇数元素,这样它们就可以并行累加,然后在循环结束时相加在一起。

一些额外的复杂性是考虑符号--将32位的data元素扩展到64位;这就是像PXOR xmm5,xmm5这样的序列;pcmpgtd xmm5、xmm4;punpckldq xmm4,xmm5完成。打开-mavx2,您将看到一个更简单的vpmovsxdq ymm5,xmm5

代码看起来也很长,因为循环已经展开,每次迭代处理8个data元素。

 类似资料:
  • 问题内容: 这是一段C ++代码,显示了一些非常特殊的行为。由于某些奇怪的原因,奇迹般地对数据进行排序使代码快了将近六倍: 不使用std::sort(data, data + arraySize);,代码将在11.54秒内运行。 使用排序的数据,代码将在1.93秒内运行。 最初,我认为这可能只是语言或编译器异常,所以我尝试了Java: 具有类似但不太极端的结果。 我首先想到的是排序将数据带入缓存,

  • 在这篇文章中,为什么处理排序数组比处理随机数组更快,它说分支预测是排序数组性能提升的原因。 但是我刚刚使用Python尝试了这个例子;我认为排序数组和随机数组没有区别(我尝试了字节数组和数组;并使用line_profile来分析计算)。 我遗漏了什么吗? 这是我的代码:

  • 我发现了这个流行的9岁左右的问题,并决定重新检查它的结果。 所以,我有AMD Ryzen 9 595 0x、Clang++10和Linux,我从问题中复制粘贴了代码,下面是我得到的: 分类-0.549702秒: 未排序-0.546554s: 我很确定的事实是,未经排序的版本被证明是快了3ms,只是噪音,但它似乎不再慢了。 那么,CPU的架构发生了什么变化(以至于不再慢一个数量级)? 以下是多次运行

  • 假设我有一个二维像素网格(4乘4像素)——我有一个和我草图一样大小的图像,它被切割成16个部分。现在我将所有16个部分加载到一个数组中。我想依次将这个数组映射到2D网格上,这样我的整体图像就可以再次正确地组合在一起。也就是说,左上角图像0.png右下角图像16.png. 我就是找不到能让我这么做的公式。例如,我知道使用可以在所有像素中运行,从左上到右下,所以我试过了。如果没有它就不能正确地坐在一起

  • 在处理接近排序的数组时,哪种算法的快速排序或合并排序性能更好?为什么?我意识到,在这种情况下,其他算法可能会比这些算法表现得更好。

  • 问题: 我必须分析时间复杂度来对几乎已排序的整数值列表进行排序(使用快速排序)。 我做了什么? 我读过SO Q1、SO Q2、SO Q3和这一本。 但是,我没有发现任何明确提到使用快速排序对k排序数组进行排序的时间复杂度的内容。 由于快速排序算法的时间复杂度取决于选择数据透视的策略,并且由于几乎排序了数据,因此有可能面临最坏情况,为了避免最坏情况,我使用了三个值(第一、中间、最后)的中位数作为这里