如何保持无法访问的代码?

Gab*_*iel 3 c++ assembly code-generation dead-code

我想编写一个函数,根据用户设置,可以执行或不执行某些可选代码.该函数是cpu密集型的,并且if中的ifs会很慢,因为分支预测器不是那么好.

我的想法是在函数的内存中复制并在我不想执行某些代码时用跳转替换NOP.我的工作示例如下:

int Test()
{
    int x = 2;
    for (int i=0 ; i<10 ; i++)
    {
        x *= 2;

        __asm {NOP}; // to skip it replace this
        __asm {NOP}; // by JMP 2 (after the goto)
            x *= 2; // Op to skip or not

        x *= 2;
    }
    return x;
}
Run Code Online (Sandbox Code Playgroud)

在我的测试主程序中,我将此函数复制到新分配的可执行内存中,并用JMP 2替换NOP,以便不执行以下x*= 2.JMP 2实际上是"跳过接下来的2个字节".

问题是每次编辑要跳过的代码并更改其大小时,我都必须更改JMP操作数.

解决这个问题的另一种方法是:

__asm {NOP}; // to skip it replace this
__asm {NOP}; // by JMP 2 (after the goto)
goto dont_do_it;
    x *= 2; // Op to skip or not
dont_do_it:
x *= 2;
Run Code Online (Sandbox Code Playgroud)

然后我想跳过或不跳过具有固定大小的goto.不幸的是,在完全优化模式下,goto和x*= 2被删除,因为它们在编译时无法访问.

因此需要保留死代码.

我正在使用VStudio 2008.

Ste*_*sop 6

您可以将分支的成本降低多达10,只需将其移出循环:

int Test()
{
    int x = 2;
    if (should_skip) {
        for (int i=0 ; i<10 ; i++)
        {
            x *= 2;
            x *= 2;
        }
    } else {
        for (int i=0 ; i<10 ; i++)
        {
            x *= 2;
            x *= 2;
            x *= 2;
        }
    }

    return x;
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,和其他类似的情况一样,这也可能激发编译器更好地优化循环体,因为它将分别考虑两种可能性而不是尝试优化条件代码,并且它不会优化任何东西死了.

如果这导致重复的代码太多而无法维护,请使用通过引用获取x的模板:

    int x = 2;
    if (should_skip) {
        doLoop<true>(x);
    } else {
        doLoop<false>(x);
    }
Run Code Online (Sandbox Code Playgroud)

并检查编译器是否内联它.

显然,这会稍微增加代码大小,这有时会引起关注.无论你采用哪种方式,如果这种改变不能产生可测量的性能改进,那么我猜你的也不会.