stackoverflow上一些有意思的C++问题

stackoverflow上一些有意思的C++问题,持续更新中…

第一、为什么处理一段已排序的数组比处理一段未排序的数组快

为什么处理一段已排序的数组比处理一段未排序的数组快

问题

此问题是测试的下面这段 C++ 代码,数组排序后,执行速率快了近 6 倍。

#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;
}
  • 如果不加std::sort(data, data + arraySize)的话,时间大概为 11.54 秒。
  • 如果加上去,只耗了 1.93 秒。

按道理说,也不应该是缓存造成的。仔细看一下这些代码,做的无非就是判断,加法这些很平常的运算。到底是什么导致了这样的差异呢?

回答

其实这是由分支预测(Branch Prediction)造成的。

分支预测的专业解释可以参考下维基上的 分支预测器。什么是分支预测器最好还是自行查资料看看,简单解释的来说,就是让 CPU 找到一个规律,可以猜到下一条要执行的是哪一条指令,然后直接跳过去,这样速度就变快了。

就以上面的代码为例,如果已经排过序了,那么就会出现下面的情况,在if (data[c] >= 128)上分支预测器很容易处理,

T = branch taken
N = branch not taken

data[] = 0, 1, 2, 3, 4, ... 126, 127, 128, 129, 130, ... 250, 251, 252, ...
branch = N  N  N  N  N  ...   N    N    T    T    T  ...   T    T    T  ...

       = NNNNNNNNNNNN ... NNNNNNNTTTTTTTTT ... TTTTTTTTTT  (easy to predict)

但是如果数据是无序的,分支预测器就没啥用了,

data[] = 226, 185, 125, 158, 198, 144, 217, 79, 202, 118,  14, 150, 177, 182, 133, ...
branch =   T,   T,   N,   T,   T,   T,   T,  N,   T,   N,   N,   T,   T,   T,   N  ...

       = TTNTTTTNTNNTTTN ...   (completely random - hard to predict)

如果你想进一步证实到底是不是分支预测影响的,你可以这么做:

替换:

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

为:

int t = (data[c] - 128) >> 31;
sum += ~t & data[c];

这样就没分支预测了(两个语句做的事情其实是等同的,就是用位运算来替换 if 语句而已)。

再次进行测试,回答者的测试环境:Core i7 920 @ 3.5 GHz

C++ - Visual Studio 2010 - x64 Release


//  Branchless - Random
seconds = 2.564

//  Branchless - Sorted
seconds = 2.587

所以基本上可以得出结论:

  • 带有分支预测的,已排序的和无序的执行时间有很大差异。
  • 不带分支预测的,基本上没有差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Cpp编程小茶馆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值