静态变量优化的C分支

静态变量优化的C分支,c,optimization,branch,control-flow,C,Optimization,Branch,Control Flow,让我先说一下,我没有分析过这段代码,它也不是一条关键路径。这主要是出于我自己的好奇心 我有一个函数,它将静态int声明/定义为一个已知的错误值,这将导致代码执行分支。然而,如果该功能成功,我确信该分支将永远不会被执行。是否对此进行编译时优化?特别是GNU/gcc/glibc 所以我有这个: static unsigned long volatile *getReg(unsigned long addr){ static int fd = -1; if (fd < 0){

让我先说一下,我没有分析过这段代码,它也不是一条关键路径。这主要是出于我自己的好奇心

我有一个函数,它将静态int声明/定义为一个已知的错误值,这将导致代码执行分支。然而,如果该功能成功,我确信该分支将永远不会被执行。是否对此进行编译时优化?特别是GNU/gcc/glibc

所以我有这个:

static unsigned long volatile *getReg(unsigned long addr){

    static int fd = -1;

    if (fd < 0){
        if (fd = open("file", O_RDWR | O_SYNC) < 0){
            return NULL;
        }
    }
}
static unsigned long volatile*getReg(unsigned long addr){
静态int fd=-1;
如果(fd<0){
如果(fd=打开(“文件”,O|RDWR | O|U同步)<0){
返回NULL;
}
}
}
因此,一旦函数成功完成(如果此函数返回null,我将退出程序),我知道fd将对所有未来调用有效,并且永远不会执行第一个分支。我知道有内置的expect()宏,所以我可以编写

if (__builtin_expect((fd<0),0){

if(\uuuuu内置\uexpect((fd将函数一分为二,在它们自己的源文件中…并让调用方担心:)

简单的回答是“不”

我的意思是,当然,你可能会玩一些小把戏,比如指向函数的指针,猴子修补你的代码等等,但这几乎肯定比仅仅做测试要慢

分支只有在被错误预测时才是昂贵的。
\u内置的expect
将安排确保该分支仅在第一次被错误预测

这里您实际上是在谈论一个或两个周期,甚至可能不是,这取决于CPU在代码附近执行的其他操作

[更新]

如果像这样的东西确实每秒被调用数百万次或数十亿次,您可以通过重构代码来处理它,以便尽早初始化
fd
,然后重复使用它,而不必费心进行测试。例如,您可以在main()顶部附近添加一个
initGlobalState();
调用,然后打开文件。(您需要一个相应的
destroyGlobalState();
来再次关闭它。)

当然,文件描述符是一个可怕的例子,因为对它所做的任何操作都将花费远远超过一个或两个周期的时间


<>在C++中,构造函数、析构函数和这种方法使得非常自然,顺便说一下,

< P> <代码> Bug BuffiTynION/<代码>只是一个提示。它帮助编译器生成更好的代码。例如,重新排列跳跃标签,使主线代码在内存中不断对齐,这使得它对代码缓存线更友好,更容易。从主内存等获取。运行配置文件引导优化甚至更好

我看不到您的代码中有任何锁定,因此我假设不应该同时从多个线程调用此函数。在这种情况下,您必须将
fd
移出函数范围,以便不应用双重检查锁定。然后,重新排列代码(这是GCC应该对分支提示执行的操作,但您知道…)。此外,如果您经常访问文件描述符,可以将其从主内存/缓存线复制到寄存器中。代码如下所示:

static int g_fd = -1;

static unsigned long volatile *getReg(unsigned long addr)
{
    register int fd = g_fd;

    if (__builtin_expect ((fd > 0), 1))
    {
on_success:
        return NULL; // Do important stuff here.
    }

    fd = open("file", O_RDWR | O_SYNC);

    if (__builtin_expect ((fd > 0), 1))
    {
        g_fd = fd;
        goto on_success;
    }

    return NULL;
}
但是请不要把这当回事。系统调用和文件I/O非常糟糕,所以像这样优化东西没有任何意义(除了一些例外)


如果你真的想调用一次,那么最好将文件打开转移到一个单独的函数中,该函数调用一次,然后再调用其他函数。是的,请查看GCC的配置文件反馈和LTO。这将帮助你在不花费太多时间的情况下获得好的结果。

解决此问题的方法之一是使用函数指针调用该方法。将函数ptr初始化为长函数,并在第一次调用结束时将其设置为无需额外初始化的版本


这就是说,这听起来像是一个绝对的维护噩梦,当然不值得避免一个分支-但是你要去掉这个分支..(当然要去掉函数内联的任何机会,这取决于函数的长度几乎肯定是有害的)对于任何好奇的人,这就是我提出的。请注意,这是一个更大的、长期运行的程序的模块。此外,它还没有经过审查,基本上是一个糟糕的黑客行为

__attribute__((noinline)) static unsigned int volatile *get_mem(unsigned int addr) {
    static void *map = 0 ;
    static unsigned prevPage = -1U ;
    static int fd = -1;
    int poss_err = 0;
    register unsigned page = addr & ~MAP_MASK ;

    if ( unlikely(fd < 0) ) {
        if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
            longjmp(mem_err, errno);
        }
    }
    if ( page != prevPage ) {
        if ( map ) {
            if (unlikely((munmap(map,MAP_SIZE) < 0))) poss_err = 1;
        }
        if (unlikely((map = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page )) == MAP_FAILED)) longjmp(mem_err, errno);

        prevPage = page ;
    }
    return (unsigned int volatile *)((char *)map+(addr & MAP_MASK));
}

static void set_reg(const struct reg_info * const r, unsigned int val)
{
    unsigned int volatile * const mem = get_mem(r->addr);
    *mem = (*mem & (~(r->mask << r->shift))) | (val << r->shift);
}

// This isn't in the final piece. There are several entry points into this module. Just an example

static int entryPoint(unsigned int value){

    if (setjmp(mem_err)!=0) {
        // Serious error
        return -1;
    }

    for (i=0; i<n; i++) {
        if (strlen(regs[i].name) == strlen(name) &&
                strncmp(regs[i].name, name, strlen (name))==0) {

            set_reg(&regs[i], value);
            return value;
        }
    }
}
\uuuuuuuuuuuuuuu属性((noinline))静态无符号int volatile*get\u mem(无符号int addr){
静态void*map=0;
静态无符号prevPage=-1U;
静态int fd=-1;
int poss_err=0;
注册无符号页面=地址&~MAP\u掩码;
如果(不太可能(fd<0)){
如果((fd=open(“/dev/mem”,O|RDWR | O|u SYNC))<0){
longjmp(mem_err,errno);
}
}
如果(第页!=上一页){
如果(地图){
如果(不太可能((munmap(map,map_SIZE)<0))poss_err=1;
}
如果(不太可能((map=mmap(0,map_大小,PROT_读,PROT_写,map_共享,fd,page))==map_失败))longjmp(mem_err,errno);
prevPage=第页;
}
返回(无符号int volatile*)((char*)map+(addr&map_MASK));
}
静态void set_reg(常量结构reg_info*常量r,无符号int val)
{
无符号int volatile*const mem=get_mem(r->addr);
*mem=(*mem&(~(r->mask-shift))|(val-shift);
}
//这不在最后一部分。这个模块有几个入口点。只是一个例子
静态int入口点(无符号int值){
if(setjmp(mem_err)!=0){
//严重错误
返回-1;
}

对于(i=0;iShort of self-modification code),我认为没有办法避免条件(或等价的函数指针)在一种情况下,您期望的行为会根据条件而有所不同!但这并不能消除条件检查。它只是将其移到其他地方。现在它需要多条指令,因为它必须查找函数地址等。即使您需要调用
getReg
100万次,条件也只测试一次
static int g_fd = -1;

static unsigned long volatile *getReg(unsigned long addr)
{
    register int fd = g_fd;

    if (__builtin_expect ((fd > 0), 1))
    {
on_success:
        return NULL; // Do important stuff here.
    }

    fd = open("file", O_RDWR | O_SYNC);

    if (__builtin_expect ((fd > 0), 1))
    {
        g_fd = fd;
        goto on_success;
    }

    return NULL;
}
__attribute__((noinline)) static unsigned int volatile *get_mem(unsigned int addr) {
    static void *map = 0 ;
    static unsigned prevPage = -1U ;
    static int fd = -1;
    int poss_err = 0;
    register unsigned page = addr & ~MAP_MASK ;

    if ( unlikely(fd < 0) ) {
        if ((fd = open("/dev/mem", O_RDWR | O_SYNC)) < 0) {
            longjmp(mem_err, errno);
        }
    }
    if ( page != prevPage ) {
        if ( map ) {
            if (unlikely((munmap(map,MAP_SIZE) < 0))) poss_err = 1;
        }
        if (unlikely((map = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, page )) == MAP_FAILED)) longjmp(mem_err, errno);

        prevPage = page ;
    }
    return (unsigned int volatile *)((char *)map+(addr & MAP_MASK));
}

static void set_reg(const struct reg_info * const r, unsigned int val)
{
    unsigned int volatile * const mem = get_mem(r->addr);
    *mem = (*mem & (~(r->mask << r->shift))) | (val << r->shift);
}

// This isn't in the final piece. There are several entry points into this module. Just an example

static int entryPoint(unsigned int value){

    if (setjmp(mem_err)!=0) {
        // Serious error
        return -1;
    }

    for (i=0; i<n; i++) {
        if (strlen(regs[i].name) == strlen(name) &&
                strncmp(regs[i].name, name, strlen (name))==0) {

            set_reg(&regs[i], value);
            return value;
        }
    }
}