gpt4 book ai didi

c - 使用 rdmsr/rdpmc 提高分支预测精度

转载 作者:行者123 更新时间:2023-12-04 04:29:46 29 4
gpt4 key购买 nike

我试图了解分支预测单元如何在 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调用,这是一个系统调用,我用系统调用污染了全局历史,因此,不是一个好的度量。

我读过 rdpmcrdmsr并且我假设由于它们只是指令,我不会污染全局历史,并且我可以一次测量单个分支指令。

但是,我不知道如何做到这一点。我有 AMD 3600 CPU。这些是我在网上找到的链接,但我不知道如何做到这一点。除此之外,我还缺少什么吗?

Intel rdpmc

AMD Performance manual

最佳答案

您已经假设 PAPI 和/或 perf_events 代码的占用空间相对较小。这是不正确的。如果您将性能计数器事件更改为“指令已停用”或“CPU 周期未停止”之类的内容,您将能够看到此操作在您的软件环境中包含多少开销。详细信息将取决于您的操作系统版本,但我预计开销将达到数百条指令/数千个周期,因为读取 perf_events 中的计数器(由 PAPI 使用)所需的内核交叉。代码路径肯定会包含它自己的分支。

如果您的内核支持“用户模式 ​​RDPMC”(CR4.PCE=1),您可以使用一条指令读取性能计数器。示例可在 https://github.com/jdmccalpin/low-overhead-timers 中找到.

即使将测量代码限制为本地 RDPMC 指令(以及用于保存结果的周围代码),测量也会破坏处理器管道。 RDPMC 是微编码指令。在 Ryzen 内核上,指令执行 20 个微操作,每 20 个周期具有一条指令的吞吐量。 (引用: https://www.agner.org/optimize/instruction_tables.pdf )

任何细粒度的测量都是具有挑战性的,因为现代处理器的乱序功能与用户代码交互的方式记录不足且难以预测。有关此主题的更多说明(也与 AMD 处理器相关)位于 http://sites.utexas.edu/jdm4372/2018/07/23/comments-on-timing-short-code-sections-on-intel-processors/

关于c - 使用 rdmsr/rdpmc 提高分支预测精度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60265006/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com