从这里我知道英特尔近年来实施了几种静态分支预测机制:
80486年龄:永远不被采取
Pentium4年龄:未采取后退/前锋
像Ivy Bridge,Haswell这样的新型CPU变得越来越无形,请参阅Matt G的实验.
英特尔似乎不想再谈论它,因为我在英特尔文档中找到的最新资料大约是十年前写的.
我知道静态分支预测(远远不是)比动态更重要,但在很多情况下,CPU将完全丢失,程序员(使用编译器)通常是最好的指南.当然,这些情况通常不是性能瓶颈,因为一旦频繁执行分支,动态预测器就会捕获它.
由于英特尔不再在其文档中明确声明动态预测机制,因此GCC的builtin_expect()只能从热路径中删除不太可能的分支.
我不熟悉CPU的设计,我不知道究竟是什么机制,目前英特尔使用其静态预测,但我还是觉得英特尔的最佳机制应该清楚地记录他的CPU",我打算去当动态预测失败,向前或向后',因为通常程序员是当时最好的指南.
更新:
我发现你提到的主题逐渐超出我的知识范围.这里涉及一些动态预测机制和CPU内部细节,我在两三天内无法学习.所以请允许我暂时退出你的讨论并充电.
这里仍然欢迎任何答案,也许会帮助更多人
compiler-construction x86 intel cpu-architecture branch-prediction
我想通过我在项目中遇到的实际问题来解释我的问题.
我正在编写ac库(其行为类似于可编程vi editor),我计划提供一系列API(总共超过20个):
void vi_dw(struct vi *vi);
void vi_de(struct vi *vi);
void vi_d0(struct vi *vi);
void vi_d$(struct vi *vi);
...
void vi_df(struct vi *, char target);
void vi_dd(struct vi *vi);
Run Code Online (Sandbox Code Playgroud)
这些API不执行核心操作,它们只是包装器.例如,我可以vi_de()像这样实现:
void vi_de(struct vi *vi){
vi_v(vi); //enter visual mode
vi_e(vi); //press key 'e'
vi_d(vi); //press key 'd'
}
Run Code Online (Sandbox Code Playgroud)
但是,如果包装器就这么简单,我必须编写20多个类似的包装器函数.
所以,我考虑实现更复杂的包装器来减少数量:
void vi_d_move(struct vi *vi, vi_move_func_t move){
vi_v(vi);
move(vi);
vi_d(vi);
}
static inline void vi_dw(struct vi *vi){
vi_d_move(vi, vi_w);
}
static inline void vi_de(struct vi *vi){
vi_d_move(vi, …Run Code Online (Sandbox Code Playgroud) 从网上的一些话来看,我知道GCC足够聪明,可以决定是否内联函数.该inline关键词是只是一个提示:
GCC可以内嵌一个共同的功能,并且可以不内联的内联函数.
但是对于我的项目中的这个功能:
struct vb_pos{
union{
struct{
int offset;
int l;
};
unsigned long long g_offset;
};
};
static inline void vi_write_vtail_smart(struct vi *vi){
struct vb_pos *vhead, *vtail, *cursor;
vhead = &vi->v_head;
vtail = &vi->v_tail;
cursor = &vi->cursor;
int curoff = vi->curr - vi->lines[vi->currl].buf;
cursor->offset = curoff;
if(cursor->g_offset >= vhead->g_offset){
*vtail = *cursor;
}
else{
*vtail = *vhead;
*vhead = *cursor;
}
}
Run Code Online (Sandbox Code Playgroud)
用-O2编译.
我检查了汇编代码,并且知道这个函数是按预期内联的.
但是,当我删除它的inline修饰符并重新编译时,我发现它并不是独行侠.它的函数体出现在最终的二进制文件中:
0000000000000000 <vi_write_vtail_smart>:
0: 48 63 47 …Run Code Online (Sandbox Code Playgroud) 这是我的测试代码:
#include<stdio.h>
static inline void foo(int a){
printf("%x\n", a);
}
int main(void){
foo(0x1234);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我认为GCC应该意识到这a是一个文字整数,并优化代码如下:
puts("1234");
Run Code Online (Sandbox Code Playgroud)
但是我得到了以下汇编代码:
?0x8048341 <main+17> push $0x1234
?0x8048346 <main+22> push $0x80484e0
?0x804834b <main+27> push $0x1
?0x804834d <main+29> call 0x8048310 <__printf_chk@plt>
Run Code Online (Sandbox Code Playgroud)
在我的项目中存在很多这样的代码,因为我一直相信GCC会为我优化,甚至在某些可以简单地使用'write()'的上下文中,我坚持使用printf,因为我认为我会从中受益缓冲机制.
现在我感到遗憾,因为削减格式字符串的开销将会扼杀任何我获得的收益.我项目中的这些代码非常低级,可能会导致性能瓶颈.