分支预测和除零

时间:2015-08-03 08:22:32

标签: c++ optimization error-handling cpu-architecture branch-prediction

我编写的代码如下所示......

if(denominator == 0){
    return false;
}
int result = value / denominator;

...当我考虑CPU中的分支行为时。

https://stackoverflow.com/a/11227902/620863这个答案说CPU将尝试正确猜测分支将以哪种方式进入,并且只有在发现分支错误时才会停止该分支停止。

但是如果CPU预测上面的分支不正确,它将在以下指令中除以零。虽然这不会发生,我想知道为什么? CPU是否实际执行除零并在执行任何操作之前等待分支是否正确,还是可以告诉它在这些情况下不应该继续?发生了什么事?

4 个答案:

答案 0 :(得分:26)

当基于预测推测性地执行分支时,CPU可以随心所欲地做任何事情。但它需要以对用户透明的方式这样做。所以它可能会分为零"如果分支预测错误,这应该是不可见的。通过相同的逻辑,它可以将写入分段写入内存,但它实际上可能不会提交它们。

作为CPU设计师,我不打算预测过去这样的错误。这可能不值得。这个错误可能意味着一个糟糕的预测,并且很快就会自行解决。

这种自由是一件好事。考虑一个简单的std::accumulate循环。分支预测器将正确预测大量跳转(for (auto current = begin, current != end; ++current),它通常会跳回到循环的开始),并且存在大量可能发生故障的内存读取(sum += *current)。但是,在前一个分支被解析之前拒绝读取内存值的CPU会慢得多。然而,在循环结束时错误预测的跳转可能会导致无害的内存故障,因为预测的分支会尝试读取缓冲区。这需要在没有明显错误的情况下解决。

答案 1 :(得分:6)

不完全是。系统不允许在错误的分支中执行指令,即使它做了错误的猜测,或者更确切地说,如果它确实不可见。基本是:

  • 机器代码中的某处有一个测试。
  • 处理器使用其中一条可能路径上的指令加载管道并可能执行内部 - 根据MSalters,某些处理器甚至可以执行两个路径(*)
  • 如果它做得很好,很好,下面的指令已预先加载到处理器缓存中或已经执行,并且所有内容都尽可能快
  • 如果做出了错误的猜测,只需清理所有内容并重新启动正确的分支。

对于与参考岗位的类比,如果开关位置不正确,火车必须立即停在路口,不能在错误的路径上前往下一站,或者如果在此之前无法停止,则没有乘客应允许进出火车

(*)Itanium处理器可以并行处理多个路径。英特尔的逻辑是,他们可以构建宽处理器(并行执行大量处理),但他们正在努力解决有效指令速率问题。通过推测性地执行两个分支,他们使用了大量的硬件(我认为它们可以在几个层次上运行,运行2 ^ N个分支)但它确实有助于明显的单核速度,因为它实际上总是在一个硬件中预测正确的分支单位 - 积分应该转到MSalters以获得该精度

答案 2 :(得分:0)

  

但是如果CPU预测上面的分支不正确,它就会分开   在以下说明中为零。但这并不会发生   我想知道为什么?

可能会发生这种情况,但问题是:它是否可以观察到?显然,这种由零推测的分裂不会也不应该“崩溃”#34; CPU,但是对于非推测性除法甚至不会发生这种情况。除以零之间存在长因果链,并且您的进程退出并显示错误消息。它有点像这样(在POSIX,x86上):

  • 负责划分的ALU或微码将除以零标记为错误。
  • 加载中断描述符#0(int 0表示x86上的除零错误)。
  • 一组寄存器(包括当前程序计数器)被压入堆栈。可能需要首先从RAM中获取相应的高速缓存行。
  • 执行中断处理程序(一段内核代码)。它在当前进程中引发了SIGFPE信号。
  • 最终,信号处理决定采取默认操作(假设您没有安装处理程序),即显示错误消息并终止该过程。
  • 这需要许多额外的步骤(例如使用设备驱动程序),直到最终用户可以观察到更改,即内存映射I / O输出的一些图形。

与简单,无差错的划分相比,这是一项很多工作,其中很多都可以推测性地执行。基本上是直到实际mmap的I / O,或直到用于推测性执行的有限资源集(例如影子寄存器和临时高速缓存行)为止。后者很可能会发生得更快,更快。在这种情况下,需要暂停推测分支,直到明确是否实际采用并且应该提交更改(一旦写入更改,然后可以释放推测执行资源),或者更改是否应该是丢弃。

重要的一点是:只要没有推测执行状态对其他线程,同一线程或其他硬件(如图形)上的其他推测分支可见,任何内容都优化。但是,实际上,MSalters绝对是正确的,因为CPU设计人员不会为这个用例进行优化。所以我同样认为,一旦设置了错误标志,真正的CPU可能只会挂起推测分支。如果错误甚至是合法的,这最多花费几个周期,甚至这不太可能,因为您描述的模式很常见。在这一点上进行推测性执行只会从更重要的案例中转移宝贵的优化资源。

(事实上,我唯一想要制作得非常快的处理器例外是我是CPU设计师,是一种特定类型的页面错误,其中页面是已知且可访问的,但是"存在&#34 ;标志被清除,因为这通常在使用虚拟内存时发生,并且不是真正的错误。但是,即使这种情况也不是非常重要,因为交换时的磁盘访问,甚至只是内存解压缩,通常更多昂贵的。)

答案 3 :(得分:0)

除零并不是什么特别的事。这是由ALU处理以产生一些效果的条件,例如为商指定特殊值。如果已启用此异常类型,它也可能引发异常。

与代码段比较

if (denominator == 0) {
    return false;
}
int result = value * denominator;

可以推测性地执行乘法,然后在不知情的情况下取消。同一个部门。不用担心。