标签: branch-prediction

if 分支比 else 分支快吗?

我发现了这个非常漂亮的信息图,它对某些操作所使用的 CPU 周期进行了粗略估计。在学习时,我注意到一个条目“if的右分支”,我认为如果满足条件,该分支将采用“if”(编辑:正如评论中指出的“右”实际上意味着“正确预测的分支” )。这让我想知道 if 分支与 else 分支相比是否存在任何(即使是很小的)速度差异。

例如,比较以下非常简洁的代码:

演示

#include <cstdio>

volatile int a = 2;

int main()
{
    if (a > 5) {
        printf("a > 5!");
        a = 2;
    } else {
        printf("a <= 5!");
        a = 3;
    }
}
Run Code Online (Sandbox Code Playgroud)

它在 x86 64 位中生成此程序集:

.LC0:
        .string "a > 5!"
.LC1:
        .string "a <= 5!"
main:
        push    rcx
        mov     eax, DWORD PTR a[rip]
        cmp     eax, 5
        jle     .L2
        mov     edi, OFFSET FLAT:.LC0
        xor     eax, eax …
Run Code Online (Sandbox Code Playgroud)

c++ performance x86 assembly branch-prediction

6
推荐指数
1
解决办法
896
查看次数

多态的成本

我在x86-64中查看下面的虚方法调用:

mov     rcx, qword ptr [x]   
mov     rax, qword ptr [rcx]
call    qword ptr [rax+8]
Run Code Online (Sandbox Code Playgroud)

以及Agner Fog的延迟表:

http://www.agner.org/optimize/instruction_tables.pdf

当我使用Ivy Bridge CPU时,我正在查看第175页.

  1. 我是否正确,前两个MOV指令都只占用2个(它们都是移动存储器来注册)CPU周期?我以为对虚拟方法的调用比这慢?

  2. 在178页的指令延迟表中,它表​​示此调用的延迟是2个CPU周期(我认为?).CALLCALL'r'(寄存器)和CALL'm'(存储器)相比,'near'意味着什么?

  3. 所以根据Fog小册子,上面的ASM确实需要6个CPU周期,我没有误解过任何东西?

编辑:我将虚函数调用更改为vtable中的第二个.

cpu x86 assembly branch-prediction

5
推荐指数
1
解决办法
424
查看次数

如何衡量Linux上单个分支的误预测?

我知道在执行程序时我可以得到分支误预测的总百分比perf stat.但是,如何获取特定分支(ifswitchC代码中的语句)的统计信息?

linux profiling branch-prediction perf

5
推荐指数
1
解决办法
875
查看次数

用分支启动函数

从优化和分支预测器的角度来看,这两个代码之间有什么区别吗?

第一:

void think_and_do(){
    if(expression){
        //Set_A of instructions
    }
    else{
         //Set_B of instructions
    }
}

int main(){
    think_and_do();
}
Run Code Online (Sandbox Code Playgroud)

第二:

void do_A(){
    //Set_A of instructions
}

void do_B(){
    //Set_B of instructions
}

int main(){
    if(expression){
        do_A();
    }
    else{
        do_B();
    }
}
Run Code Online (Sandbox Code Playgroud)

c++ branch-prediction

5
推荐指数
1
解决办法
119
查看次数

C ++中的可移植分支预测提示

在StackOverflow上已经解决了两次分支预测的问题。但是,我没有找到想要的答案。
在优化阶段,我需要避免分支预测错误。我需要做一些验证。看起来像:

if(!successCondition)
    { throw Something(); }
Run Code Online (Sandbox Code Playgroud)

当然,在大多数情况下会发生的正常预期工作流中,我们不会抛出异常,因此也不会输入if。

我知道,在常见的if / else范例中,我们可以通过将最可能的分支放在if中,而将不太可能的分支放在else中来提示编译器(Portable分支预测提示)。但是我不想(由于可读性)链接ifs:

if(successCondition)
    { whatever(); }
else
    { throw Something(); }
Run Code Online (Sandbox Code Playgroud)

因此,我了解到,默认情况下,编译器将支持if中的条目,并且我会得到分支预测错误。

我知道gcc具有优化代码的特定功能,这在Linux内核中不太可能被调用(支持分支的编程)。但这不是可移植的,我需要编写代码。

有没有办法在C ++中具有可移植的正确分支预测?

c++ branch-prediction

5
推荐指数
1
解决办法
955
查看次数

当Skylake CPU错误预测分支时会发生什么?

我试图详细了解当分支预测错误时,skylake CPU管道的各个阶段中的指令会发生什么,以及从正确的分支目标开始执行指令的速度如何。

因此,让我们在这里将两个代码路径标记为红色(一个预测但未实际采用)和绿色(一个已预测但未预期)。所以问题是:1.在红色指令开始被丢弃之前,分支必须经过管道多远(以及在管道的哪个阶段被丢弃)?2.绿色指令(在分支到达的流水线阶段方面)多久可以开始执行?

我看过Agner Fogg的文档和许多讲义,但这些观点并不清楚。

x86 intel cpu-architecture speculative-execution branch-prediction

5
推荐指数
1
解决办法
369
查看次数

为什么Swift在for-in循环中使用下标语法比使用直接访问元素更快?

我读了着名的为什么处理排序数组比未排序数组更快?我决定玩游戏并尝试其他语言,如Swift.我对2个非常相似的代码片段之间的运行时间差异感到惊讶.

在Swift中,可以在for-in循环中以直接方式或使用下标访问数组中的元素.例如这段代码:

for i in 0..<size {
    sum += data[i]
}
Run Code Online (Sandbox Code Playgroud)

可写:

for element in data {
    sum += element
}
Run Code Online (Sandbox Code Playgroud)

size所述data长度和data可累加元件的阵列.

所以,我只是在Swift(代码吼叫)中实现了与我在第一段中提到的问题相同的算法,令我惊讶的是第一种方法比第二种方法快大约5倍.

我真的不知道后台下标的实现,但我认为直接访问Swift for-in循环中的元素只是语法糖.


我的问题是两种for-in语法之间有什么区别以及为什么使用下标更快?

这里是计时器的细节.我在2015年初的MacBook Air上使用Xcode 9.4.1和Swift 4.1以及Commande Line项目.

// Using Direct Element Access
Elapsed Time: 8.506288427
Sum: 1051901000
Run Code Online (Sandbox Code Playgroud)

VS

// Using Subscript
Elapsed Time: 1.483967902
Sum: 1070388000
Run Code Online (Sandbox Code Playgroud)

奖金问题:为什么Swift中的执行速度比C++慢100倍(两者都在Xcode项目的同一台Mac上执行)?例如,C++中的100,000次重复几乎与Swift中的1,000次重复相同.我的第一个猜测是,Swift是一种比C++更高级的语言,而Swift运行更多的安全检查.


这是我使用的Swift代码,我只修改了第二个嵌套循环:

import Foundation
import GameplayKit

let size = 32_768
var data = [Int]()
var sum  = 0 …
Run Code Online (Sandbox Code Playgroud)

optimization performance branch-prediction swift

5
推荐指数
1
解决办法
351
查看次数

为什么cpu"insn per cycle"在类似的cpu中有所不同以及"MONITOR-MWAIT"如何在Linux中运行?

背景: 我有2个服务器,所有os内核版本都是4.18.7,它有CONFIG_BPF_SYSCALL = y

我创建了一个shell脚本'x.sh'

i=0 
while (( i < 1000000 )) 
do (( i ++ )) 
done
Run Code Online (Sandbox Code Playgroud)

并运行命令: perf stat ./x.sh

所有的shell版本都是"4.2.6(1)-release"

S1: CPU - Intel(R)Xeon(R)CPU E5-2630 v4 @ 2.20GHz,以及微码 - 0xb00002e和perf stat结果

   5391.653531      task-clock (msec)         #    1.000 CPUs utilized          
             4      context-switches          #    0.001 K/sec                  
             0      cpu-migrations            #    0.000 K/sec                  
           107      page-faults               #    0.020 K/sec                  
12,910,036,202      cycles                    #    2.394 GHz                    
27,055,073,385      instructions              #    2.10  insn per cycle         
 6,527,267,657      branches                  # 1210.624 M/sec                  
    34,787,686      branch-misses             #    0.53% of all branches …
Run Code Online (Sandbox Code Playgroud)

cpu linux-kernel branch-prediction perf

5
推荐指数
1
解决办法
180
查看次数

幽灵的内部运作(v2)

我已经做了一些关于Spectre v2的阅读,显然你得到了非技术性的解释.彼得科德斯有一个更深入的解释,但它没有完全解决一些细节.注意:我从未进行过Spectre v2攻击,因此我没有亲身体验.我只读过关于这个理论的内容.

我对Spectre v2的理解是你做了一个间接的分支误预测if (input < data.size).如果间接目标数组(我不太确定它的细节 - 即为什么它与BTB结构分开) - 在解码时重新检查间接分支的RIP - 不包含预测那么它将插入新的跳转RIP(分支执行最终将插入分支的目标RIP),但是现在它不知道跳转的目标RIP,因此任何形式的静态预测都不起作用.我的理解是它始终预测不会采用新的间接分支,并且当端口6最终计算出跳转目标RIP并预测它将使用BOB回滚并使用正确的跳转地址更新ITA然后更新本地和全局分支历史寄存器和相应的饱和计数器.

黑客需要训练饱和计数器,以便始终预测所采取的操作,我想,通过if(input < data.size)在循环中多次运行,其中input设置为确实小于data.size(相应地捕获错误)和循环的最后一次迭代,input超过data.size(例如1000); 将预测间接分支,它将跳转到发生缓存加载的if语句的主体.

if语句包含secret = data[1000](包含秘密数据的特定内存地址(data [1000]),目标是从内存加载到缓存)然后将推测性地将其分配给加载缓冲区.前面的间接分支仍在分支执行单元中,等待完成.

我相信前提是在错误预测中刷新加载缓冲区之前需要执行加载(分配行填充缓冲区).如果已为其分配了行填充缓冲区,则无法执行任何操作.有意义的是没有取消行填充缓冲区分配的机制,因为行填充缓冲区必须在将其返回到加载缓冲区之后存储到缓存之前挂起.这可能导致行填充缓冲区变得饱和,因为它不是在需要时解除分配(保持在那里以便其他负载的速度到同一地址,但在没有其他可用的行缓冲区时解除分配).在收到一些不会发生刷新的信号之前,它将无法解除分配,这意味着它必须暂停以执行前一个分支,而不是立即使行填充缓冲区可用于其他逻辑核心的存储.这种信令机制可能难以实现,也许它并没有超出他们的想法(史前思考者),并且如果分支执行需要足够的时间来挂起行填充缓冲区以引起性能影响,即它也会引入延迟,即在循环的最后一次迭代之前data.size有目的地从cache(CLFLUSH)中刷新,这意味着分支执行可能需要多达100个周期.

我希望我的想法是正确的,但我不是百分百肯定.如果有人有任何要添加或更正,那么请做.

x86 intel cpu-architecture branch-prediction spectre

5
推荐指数
1
解决办法
250
查看次数

How to speed up dynamic dispatch by 20% using computed gotos in standard C++

Before you down-vote or start saying that gotoing is evil and obsolete, please read the justification of why it is viable in this case. Before you mark it as duplicate, please read the full question.

I was reading about virtual machine interpreters, when I stumbled across computed gotos . Apparently they allow significant performance improvement of certain pieces of code. The most known example is the main VM interpreter loop.

Consider a (very) simple VM like this:

#include <iostream> …
Run Code Online (Sandbox Code Playgroud)

c++ optimization virtual-functions goto branch-prediction

5
推荐指数
1
解决办法
117
查看次数