【问题标题】:Is branch prediction still significantly speeding up array processing? [closed]分支预测是否仍在显着加快数组处理速度? [关闭]
【发布时间】:2019-09-02 06:01:21
【问题描述】:

我正在阅读一篇关于 why is it faster to process a sorted array than an unsorted array? 的有趣帖子,并看到 @mp31415 发表的评论说:

仅作记录。在 Windows / VS2017 / i7-6700K 4GHz 上,两个版本之间没有区别。两种情况都需要 0.6 秒。如果外部循环中的迭代次数增加 10 倍,则两种情况下的执行时间也会增加 10 倍至 6 秒

所以我在online c/c++ compiler(我想是现代服务器架构)上尝试了它,我得到,排序和未排序分别为~1.9s 和~1.85s,差别不大,但可重复。

所以我想知道现代建筑是否仍然如此? 问题是从2012年开始的,离现在不远了...... 或者我哪里错了?


重新打开的问题精度:

  • 请忘记我添加 C 代码作为示例。这是一个可怕的错误。不仅代码错误,发布它还会误导那些只关注代码本身而不是问题的人。

  • 当我首先尝试上面链接中使用的 C++ 代码时,只得到了 2% 的差异(1.9s 和 1.85s)。

  • 我的第一个问题和意图是关于上一篇文章、它的 c++ 代码和@mp31415 的评论。

  • @rustyx 发表了一个有趣的评论,我想知道它是否可以解释我所观察到的。

    有趣的是,调试版本在排序/未排序之间表现出 400% 的差异,而发布版本的差异最多为 5% (i7-7700)。

换句话说,我的问题是:

  • 为什么上一篇文章中的 c++ 代码的性能不如之前的 OP 声称的那么好?

精确:

  • release build 和 debug build 的时间差能解释一下吗?

【问题讨论】:

  • 考虑到这里提供的GetMyRand() 函数,我怀疑不是很随机。
  • 单个测试用例并不能证明您认为代码在现代架构中运行方式不同的假设是正确的。连续几次快速调用GetTimeStamp() 每次都会返回相似的值,这意味着您的GetMyRand() 不是特别随机的。因此,您的结果很可能是您选择数据的结果。
  • 即使 GetMyRand() % 0xff.... 返回了一个好的随机无符号整数值,在几乎所有情况下,将其 '
  • 我要关闭这个问题(并清理 cmets),因为您在收到答案后多次对问题进行了重大更改,包括删除所有问题中显示的源代码以及答案所指的源代码。这个问题已经变成了一个不清楚的、无定形的团块,没有人可以回答——他们也不应该回答,因为如果以历史为指导的话,它很可能会再次改变。您需要确定自己实际要问的是什么,呈现这些信息,然后别管它。
  • 是的。然后你从他下面改变了问题。那就是问题所在。我完全有能力回答有关分支预测和 x86 架构的问题。事实上,我是该特定主题的专家。但我什至不想尝试回答这个问题,因为你一直在修改问题。这不适用于堆栈溢出;这是一个问答网站,一旦发布了答案,问题需要保持相对稳定,否则整个讨论会变得不连贯。

标签: c++ c performance branch-prediction


【解决方案1】:

你是as-if rule的受害者:

...为了模拟(仅)抽象机的可观察行为,需要符合规范的实现...

考虑被测函数...

const size_t arraySize = 32768;
int *data;

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

还有generated assembly(VS 2017,x86_64 /O2 模式)

机器执行你的循环,而是执行一个类似的程序来做这个:

long long test()
{
    long long sum = 0;
    // Primary loop
    for (size_t c = 0; c < arraySize; ++c)
    {
        for (size_t i = 0; i < 20000; ++i)
        {
            if (data[c] >= 128)
                sum += data[c] * 5;
        }
    }
    return sum;
}

观察优化器如何颠倒循环顺序并击败您的基准。

显然后一个版本对分支预测器更友好。

您可以反过来通过在外循环中引入依赖项来破坏循环提升优化:

long long test()
{
    long long sum = 0;
    for (size_t i = 0; i < 100000; ++i)
    {
        sum += data[sum % 15];  // <== dependency!
        // Primary loop
        for (size_t c = 0; c < arraySize; ++c)
        {
            if (data[c] >= 128)
                sum += data[c];
        }
    }
    return sum;
}

现在这个版本再次展示了排序/未排序数据之间的巨大差异。在我的系统 (i7-7700) 上 1.6s 与 11s(或 700%)。

结论:当我们面临前所未有的流水线深度和指令级并行性时,分支预测器比以往任何时候都更加重要。

【讨论】:

    猜你喜欢
    • 2019-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 2011-03-22
    • 1970-01-01
    相关资源
    最近更新 更多