我试图了解分支预测单元如何在CPU中工作。
我使用过papi
,也使用过Linux的perf-events
,但它们都不能给出准确的结果(就我而言)。
这是我的代码:
void func(int* arr, int sequence_len){ for(int i = 0; i < sequence_len; i++){ // region starts if(arr[i]){ do_sth(); } // region ends } }
我的数组由0和1组成。它具有大小为
sequence_len
的图案。例如,如果我的尺寸为8,则其样式为0 1 0 1 0 0 1 1
或类似的样式。
审判1:
我试图了解CPU如何预测那些分支。因此,我使用了papi并为错误的分支预测设置了性能计数器(我知道它也计算间接分支)。
int func(){ papi_read(r1); for(){ //... same as above } papi_read(r2); return r2-r1; } int main(){ init_papi(); for(int i = 0; i < 10; i++) res[i] = func(); print(res[i]); }
我看到的是输出(序列长度为200)
100 #iter1 40 #iter2 10 #iter3 3 0 0 #...
因此,起初,CPU会盲目地预测序列,只有一半时间成功。在接下来的迭代中,CPU可以预测得越来越好。经过一些迭代之后,CPU可以完美地猜测出来。
审判2
我想看看,在哪个数组索引上发生CPU错误预测。
int* func(){ int* results; for(){ papi_read(r1); if(arr[i]) do_sth(); papi_read(r2); res[i] = r2-r1; } return res; } int main(){ init_papi(); for(int i = 0; i < 10; i++) res[i] = func(); print(res[i]); }
预期结果:
#1st iteration, 0 means no mispred, 1 means mispred 1 0 0 1 1 0 0 0 1 1 0... # total of 200 results Mispred: 100/200 #2nd iteration 0 0 0 0 1 0 0 0 1 0 0... # total of 200 results Mispred: 40/200 # it learned from previous iteration #3rd iteration 0 0 0 0 0 0 0 0 1 0 0... # total of 200 results Mispred: 10/200 # continues to learn #...
接收结果:
#1st iteration 1 0 0 1 1 0 0 0 1 1 0... # total of 200 results Mispred: 100/200 #2nd iteration 1 0 0 0 1 1 0 1 0 0 0... # total of 200 results Mispred: 100/200 # it DID NOT learn from previous iteration #3rd iteration 0 1 0 1 0 1 0 1 1 0 0... # total of 200 results Mispred: 100/200 # NO LEARNING #...
我的观察
[当我在for循环之外测量错误预测时,我可以看到CPU从其错误预测中学习。但是,当我尝试测量单个分支指令的错误预测时,CPU要么无法学习,要么我测量错误。
我的解释
我给出200作为序列长度。 CPU具有一个小型分支预测器,例如Intel中的2-3位饱和计数器,以及一个大型全局分支预测器。当我在环路外进行测量时,会给测量引入更少的噪声。噪音越小,我的意思是papi
通话。
考虑这一点:环路测量之外
全局历史为:papi_start, branch_outcome1, branch_outcome2, branch_outcome3, ..., papi_end, papi_start (2nd loop of main iteration), branch_outcome1, ...
因此,分支预测变量以某种方式在同一分支中找到模式。
但是,如果我尝试测量单个分支指令,则全局历史记录为:papi_start, branchoutcome1, papiend, papistart, branchoutcome2, papiend...
因此,我正在向全球历史介绍越来越多的分支机构。我认为全局历史记录不能容纳许多分支条目,因此,它无法在所需的if语句(分支)中找到任何相关性/模式。
结果
我需要测量单个分支的预测结果。我知道,如果我不过多引入papi,CPU可以学习200模式。我查看了papi调用,并且看到了许多for循环(如果有条件的话)。
这就是为什么我需要更好的测量。我尝试了linux perf-event
,但是它进行了ioctl
调用,这是一个系统调用,并且我使用系统调用来污染全局历史记录,因此不是一个很好的衡量标准。
我已经阅读了rdpmc
和rdmsr
指令,并且由于它们仅是指令,因此我不会污染全局历史记录,并且可以一次测量单个分支指令。
但是,我不知道该怎么做。我有AMD 3600 CPU。这些是我在网上找到的链接,但我不知道该怎么做。除此之外,我还缺少什么吗?
我试图了解分支预测单元如何在CPU中工作。我已经使用了papi和linux的perf-events,但是它们都不能给出准确的结果(就我而言)。这是我的代码:...
您已经假设PAPI和/或perf_events代码具有相对较小的占用空间。这是不正确的。如果将性能计数器事件更改为“指令已退休”或“ CPU周期未暂停”之类的内容,则可以查看此操作在软件环境中包含多少开销。详细信息将取决于您的OS版本,但是我希望开销在数百个指令/数千个周期中,因为读取perf_events(由PAPI使用)中的计数器所需的内核交叉。代码路径肯定会包含其自己的分支。
我发现如何使用Linux perf_event_open()
和rdpmc
并不明显。