我正在处理的应用程序有大量的 if 语句,其特点是在任何一次执行中,90% 的时间只有一个分支被执行。
现在,我可以通过执行以下操作来测试分支预测对特定 CPU 的单个 if 语句的影响:-
#include <iostream>
#include <stdlib.h>
using namespace std;
int main() {
int a;
cin>>a;
srand(a);
int b;
long count=0;
for (int i=0; i<10000; i++) {
for (int j=0; j<65535; j++) {
b = rand() % 30 + 1;
if (b > 15) // This can be changed to get statistics for different %-ages
count += (b+10);
}
}
cout << count <<"\n";
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,有没有一种方法可以在给定 CPU 的实际大型应用程序中使用多个 if 语句来测试分支预测的可扩展性和影响?
基本上,我希望能够弄清楚分支错误预测对各种 CPU 造成的损失及其对应用程序的影响。
所以我正在创建一个库,它将有一个类 someBase {}; 这将由许多类的下游用户派生。
class someBase {
public:
virtual void foo()=0;
};
Run Code Online (Sandbox Code Playgroud)
我还有一个指向 someBase 的指针向量,我正在这样做:-
vector <someBase*> children;
// downstream user code populates children with some objects over here
for (i=0; i<children.size(); i++)
children[i]->foo();
Run Code Online (Sandbox Code Playgroud)
现在分析表明,对虚拟调用的分支错误预测是我代码中的(几个)瓶颈之一。我想要做的是以某种方式访问对象的 RTTI 并使用它根据类类型对子项的向量进行排序,以改善指令缓存局部性和分支预测。
关于如何做到这一点的任何建议/解决方案?
要记住的主要挑战是:-
1.) 我真的不知道哪些或多少类将从 someBase 派生。假设,我可以在某个公共文件中的某个地方有一个全局枚举,下游用户可以编辑以添加他们自己的类类型,然后对其进行排序(基本上是实现我自己的 RTTI)。但这是一个丑陋的解决方案。
2.) PiotrNycz 在下面的回答中建议使用 type_info。但是,为此只定义了 != 和 ==。关于如何在 type_info 上导出严格弱排序的任何想法?
3.) 我真的很想改进分支预测和指令缓存局部性,所以如果有替代解决方案,那也将受到欢迎。
根据英特尔的说法,删除分支是优化C代码以在紧密循环中使用的最有效方法之一。但是,链接页面中的示例仅涵盖循环展开和将不变分支移至循环外部。
是否有其他且不同的(之前和之后)分支移除示例进行优化?
我在StackOverflow上读到了这个
if(someCondition)
{
someCode();
}
else
{
alternateCode();
}
Run Code Online (Sandbox Code Playgroud)
由于易受分支错误预测的影响,效率可能很低(例如,请参阅此问题).
所以是一个switch构造,例如,
switch (someCondition)
{
case (someCase):
something();
break;
case (otherCase):
someOtherInstructions();
break;
default:
defaultAction();
break;
}
Run Code Online (Sandbox Code Playgroud)
在这方面有什么不同(除了我允许三种可能性的事实)?
language-agnostic optimization performance compiler-optimization branch-prediction
有没有办法确定或任何资源,我可以找到Haswell,Sandy Bridge,Ivy Bridge和Skylake Intel处理器的分支目标缓冲区大小?
从阅读本文开始,我看到了接下来的两个引号:
第一个引用:
不可预测的分支行为的典型情况是比较结果取决于数据.
第二个引用:
没有分支意味着没有错误的预测
对于我的项目,我处理依赖数据,并执行许多if和switch语句.我的项目与之相关,Big Data因此必须尽可能高效.所以我想对用户提供的数据进行测试,看看分支预测是否会减慢我的程序或帮助.从阅读这里:
误预测延迟在10到20个时钟周期之间.
让我最震惊的是:
删除分支不仅可以提高代码的运行时性能,还可以帮助编译器优化代码.
为什么要使用分支预测?
有没有办法强制编译器生成没有分支的汇编代码?或者禁用分支预测以便CPU?所以我可以比较两个结果?
有没有办法将以下 C 代码转换为没有任何条件语句的内容?我已经分析了我的一些代码,并注意到它在与这个非常相似的 if 语句上有很多分支未命中。
int cond = /*...*/;
int a = /*...*/;
int b = /*...*/;
int x;
if (cond) {
x = a;
} else {
x = b;
}
Run Code Online (Sandbox Code Playgroud) 我在 Python 中实现了一个物理模拟(大部分繁重的工作都是在数值库中完成的,因此性能足够好)。现在该项目已经有所增长,我通过在模拟过程中不会改变的参数添加了额外的功能。随之而来的是让程序根据它们的值做一件事或另一件事的必要性,即相当多的 if-else 分散在代码中。
我的问题很简单:Python 是否实现了某种形式的分支预测?我会显着降低性能还是解释器足够聪明以看到某些参数永远不会改变?在一个被调用一百万次的函数内有一个常量 if-else,是每次都会评估条件还是发生一些魔法?当没有简单的方法可以完全删除条件时,有没有办法给解释器一些提示并支持/模拟分支预测?
我正在尝试在我的Kabe湖7600U上创建一个光谱线(cfr。Henry Wong),正在运行CentOS 7。
我的specpoline版本如下(cfr。spec.asm):
specpoline:
;Long dependancy chain
fld1
TIMES 4 f2xm1
fcos
TIMES 4 f2xm1
fcos
TIMES 4 f2xm1
%ifdef ARCH_STORE
mov DWORD [buffer], 241 ;Store in the first line
%endif
add rsp, 8
ret
Run Code Online (Sandbox Code Playgroud)
此版本与黄宏Henry的版本不同,流程被转移到建筑路径中。当原始版本使用固定地址时,我将目标传递到堆栈中。
这样,add rsp, 8将删除原始的寄信人地址并使用人工地址。
在函数的第一部分中,我使用一些旧的FPU指令创建了一个长延迟依赖关系链,然后创建了一个独立的链,试图欺骗CPU返回堆栈预测变量。
使用FLUSH + RELOAD 1将specpoline插入到配置文件上下文中,同一程序集文件还包含:
buffer
一个连续的缓冲区,它跨越256个不同的高速缓存行,每个高速缓存行之间用GAP-1行分隔开来,总共为256*64*GAP字节。
GAP用于防止硬件预取。
随后是图形描述(每个索引紧接另一个)。
timings
256个DWORD数组,每个条目保存访问F + R缓冲区中相应行所需的时间(以核心周期为单位)。
flush
一个小功能,可以触摸F + R缓冲区的每一页(带有存储,请确保COW在我们这一边)并逐出指定的行。
“个人资料”
标准配置文件功能 …
assembly x86-64 cpu-architecture speculative-execution branch-prediction
我试图了解分支预测器条目何时失效。
以下是我做过的实验:
代码1:
start_measure_branch_mispred()
while(X times):
if(something something):
do_useless()
endif
endwhile
end_measurement()
store_difference()
Run Code Online (Sandbox Code Playgroud)
因此,我多次运行此代码。我可以看到,在第一次运行之后,错误预测率会降低。分支预测器学习如何正确预测。但是,如果我一次又一次地运行这个实验(即通过写入./experiment终端),所有的第一次迭代都是从高误预测率开始的。因此,在每次执行时,它们的分支预测单元都会conditional branches失效。我正在使用nokaslr并且我已禁用ASLR. 我也在一个隔离的核心上运行了这个实验。我已经运行了几次这个实验,以确保这是行为(即不是因为噪音)。
我的问题是:程序停止执行后,CPU 是否会使分支预测单元失效?或者这是什么原因?
我做的第二个实验是:
代码 2:
do:
start_measure_branch_mispred()
while(X times):
if(something something):
do_useless()
endif
endwhile
end_measurement()
store_difference()
while(cpu core == 1)
Run Code Online (Sandbox Code Playgroud)
在这个实验中,我从两个不同的终端运行不同的进程。第一个固定在core 1这样它就可以在核心 1 上运行,它会做这个实验,直到我停止它(通过杀死它)。然后,我从另一个终端运行第二个进程,并将该进程固定到不同的核心。由于这个进程在不同的核心中,它只会执行 1 次 do-while 循环。如果第二个进程固定到第一个的兄弟内核(相同的物理内核),我看到在第一次迭代中,第二个进程猜测几乎正确。如果我将第二个进程固定到另一个不是第一个进程的同级内核,那么第二个进程的第一次迭代会产生更高的错误预测。这是预期的结果,因为同一物理核心上的虚拟核心共享相同的分支预测单元(这是我的假设)。所以,
据我了解,由于 CPU 没有完成第一个进程(执行繁忙循环的核心 1 进程),分支预测条目仍然存在,第二个进程可以从中受益。但是,在第一个中,从运行到运行,我得到了更高的错误预测。
编辑:当其他用户要求提供代码时,它就在这里。您需要从这里下载性能事件标头代码
编译: $(CXX) -std=c++11 -O0 main.cpp -lpthread -o experiment
编码:
#include "linux-perf-events.h"
#include <algorithm>
#include <climits>
#include <cstdint>
#include …Run Code Online (Sandbox Code Playgroud)