使用goto进行优化

时间:2017-01-12 09:01:24

标签: c++ goto

如果使用goto是优化的好习惯,我徘徊。我知道这是一种野蛮的事情,但是,认真的。

例如写下:

switch(command[0].cmd)
{
    case 0: // turn off
        s::s_off();
        S_time = command[0].length;
    break;
    case 1: // turn on
        s::s_on();
        S_time = command[0].length;
    break;
    case 4: // nop

    break;
}
像这样:

switch(command[0].cmd)
{
    case 0: // turn off
        s::s_off();
        goto a;
    break;
    case 1: // turn on
        s::s_on();
        goto a;
    break;
    case 4: // nop
        goto b;
    break;
}

a:
S_time = command[0].length;
b:

4 个答案:

答案 0 :(得分:5)

确实,如果可能的话,避免goto是明智的,并且相信编译器会为您进行优化。即使在您的情况下,也有一种替代方案可以避免代码重复:

/*possibly inline*/ void foo(/*pass necessary parameters*/)
{
    switch(command[0].cmd){
    case 0: // turn off
        s::s_off();
        break;
    case 1: // turn on
        s::s_on();
        break;
    case 4: // nop
        return;       
    }
    S_time = command[0].length;
}

答案 1 :(得分:3)

就优化而言,分析是您可以做的最好的。但是,让我们看看生成的组件,因为这也是有用的。我将使用以下虚拟声明:

namespace s {
    void s_on();
    void s_off();
};

struct Command {
    int cmd;
    int length;
};

int S_time;

使用Clang在-O3编译的代码的第一个版本产生:

foo(Command*):                        # @foo(Command*)
        push    rbx
        mov     rbx, rdi
        mov     eax, dword ptr [rbx]
        cmp     eax, 1
        je      .LBB0_3
        test    eax, eax
        jne     .LBB0_5
        call    s::s_off()
        jmp     .LBB0_4
.LBB0_3:
        call    s::s_on()
.LBB0_4:
        mov     eax, dword ptr [rbx + 4]
        mov     dword ptr [rip + S_time], eax
.LBB0_5:
        pop     rbx
        ret

第二个版本goto生成:

foo2(Command*):                       # @foo2(Command*)
        push    rbx
        mov     rbx, rdi
        mov     eax, dword ptr [rbx]
        cmp     eax, 4
        je      .LBB1_6
        cmp     eax, 1                # These two instructions
        je      .LBB1_4               # weren't here in the first version
        test    eax, eax
        jne     .LBB1_5
        call    s::s_off()
        jmp     .LBB1_5
.LBB1_4:
        call    s::s_on()
.LBB1_5:
        mov     eax, dword ptr [rbx + 4]
        mov     dword ptr [rip + S_time], eax
.LBB1_6:
        pop     rbx
        ret

不像其他一些案例那样清晰,但存在一个差异:第一个版本仅将command[0].cmd01进行比较,但第二个版本我们将其与01 4进行比较。更少的代码重复并不一定意味着更优化的代码:您实际上阻碍了优化器并使其为4生成了无用的特殊情况。

goto不是低级别的工具,它充满了其批评者所描述的神奇的低级优化光环。它只是一个非常基本的流量控制工具,当其他工具没有削减它时,它在C ++中很少(但仍有一些!)使用。当然,它可以用于优化,但不比其他任何更好或更容易。

答案 2 :(得分:1)

建议不要经常使用goto。但它不是“邪恶的”,并且在许多情况下更容易放弃1 goto而不是更复杂的逻辑只是为了避免“邪恶”。在你的例子中,单个goto就足够了:

switch(command[0].cmd)
{
    case 0: // turn off
        s::s_off();
        break;

    case 1: // turn on
        s::s_on();
        break;

    case 4: // nop
        goto b; // break after goto is useless
}

S_time = command[0].length;
b:

答案 3 :(得分:0)

while的使用应该是使代码更清晰。不是更复杂。这就是为什么例如goto从一组深度嵌套的循环或if条件有时可以接受,但使用goto来“优化”代码就像在这个问题的例子中一样并不是特别好的原因 - 它不会使代码更清晰,反之亦然。

还有OFTEN的其他解决方案 - 将某些功能分解为更小的功能[无论如何通常都是好事]或重构代码。但是如果你因为“不惜一切代价避免转换”而得到更复杂的代码,那么你可能会采取错误的方式。

这个问题举例说明了使用goto MAY确实比在问题本身中选择的更好的选择: What are some better ways to avoid the do-while(0); hack in C++?