Histuries
Histuries

Reputation: 45

goto Optimization Refactor

I have a "MyFunction" I keep obsessing over if I should or shouldn't use goto on it and in similar (hopefully rare) circumstances. So I'm trying to establish a hard-and-fast habit for this situation. To-do or not-to-do.

int MyFunction()
{   if (likely_condition)
    {
    condition_met:
        // ...
        return result;
    }
    else /*unlikely failure*/
    {   // meet condition
        goto condition_met;
    }
}

I was intending to net the benefits of the failed conditional jump instruction for the likely case. However I don't see how the compiler could know which to streamline for case probability without something like this.

  1. it works right?
  2. are the benefits worth the confusion?
  3. are there better (less verbose, more structured, more expressive) ways to enable this optimization?

Upvotes: 4

Views: 362

Answers (4)

Mark B
Mark B

Reputation: 96311

  1. It looks like the code should work as you expect as long as condition_met: doesn't skip variable initializations.

  2. No, and you don't even know that the obfuscated version compiles into more optimal code. Compiler optimizations (and processor branch prediction) are getting very smart in recent times.

3.

int MyFunction()
{
    if (!likely_condition)
    {
        // meet condition
    }

    condition_met:
        // ...
    return result;
}

or, if it helps your compiler (check the assembly)

int MyFunction()
{
    if (likely_condition); else
    {
        // meet condition
    }

    condition_met:
        // ...
    return result;
}

Upvotes: 4

πάντα ῥεῖ
πάντα ῥεῖ

Reputation: 1

I would highly recommend using the __builtin_expect() macro (GCC) or alike for your particular C++ compiler (see Portable branch prediction hints) instead of using goto:

int MyFunction()
{   if (__builtin_expect(likely_condition))
    {
        // ...
        return result;
    }
    else /*unlikely failure*/
    {   // meet condition
    }
}

As others also mentioned goto is error prone and evil from the bones.

Upvotes: 1

Jerry Coffin
Jerry Coffin

Reputation: 490768

It appears to me that the optimization you're trying to do is mostly obsolete. Most modern processors have branch prediction built in, so (assuming it's used enough to notice) they track how often a branch is taken or not and predict whether the branch is likely to be taken or not based on its past pattern of being taken or not. In this case, speed depends primarily on how accurate that prediction is, not whether the prediction is for taken vs. not taken.

As such, you're probably best off with rather simpler code:

int MyFunction() {   
    if (!likely_condition) {
        meet_condition();
    }
    // ...
    return result;
}

Upvotes: 5

Ben Jackson
Ben Jackson

Reputation: 93930

A modern CPU will take that branch either way with equal performance if it makes the correct branch prediction. So if that is in an inner loop, the performance of if (unlikely) { meet condition } common code; will match what you have written.

Also, if you spell out the common code in both branches the compiler will generate code that is identical to what you have written: The common case will be emitted for the if clause and the else clause will jmp to the common code. You see this all the time with simpler terminal cases like *out = whatever; return result;. When debugging it can be hard to tell which return you're looking at because they've all been merged.

Upvotes: 4

Related Questions