4

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?
Jens Björnhager
  • 5,632
  • 3
  • 27
  • 47
Histuries
  • 45
  • 4
  • 5
    Why can't you refactor it into return conditionMet(); in both cases? I assume your result is computed somehow, so why not put it into a function? –  Oct 31 '12 at 18:16
  • If you are compiling under gcc, you can hint to the compiler which branch is more likely using the built-in `__builtin_expect` hint. – cdhowie Oct 31 '12 at 18:17
  • 3
    Please don't use `goto` at all! – Johannes Mittendorfer Oct 31 '12 at 18:17
  • 2
    Do you actually know that this optimization helps measurably in a real bottleneck in your code? If not, the answer to question 2 is pretty obvious, and that makes the rest of your questions irrelevant. – abarnert Oct 31 '12 at 18:25
  • Before trying to optimize something, first profile it to check if it really matters. – Etienne de Martel Oct 31 '12 at 18:31
  • 3
    Obligatory [xkcd](http://www.xkcd.com/292/) – Praetorian Oct 31 '12 at 18:31
  • Counter suggestion: how about obsessing over writing readable code instead? – fredoverflow Oct 31 '12 at 18:52
  • 1
    @EtiennedeMartel and anyone: [_'Premature optimization is the root of all evil'_ (Donald Knuth)](http://c2.com/cgi/wiki?PrematureOptimization) – πάντα ῥεῖ Oct 31 '12 at 18:54
  • @0A0D: dodging multiple return points, running from code redundancy and chasing an optimization. was missing information on how a compiler deals with these situations. i'm all better now, thanx. – Histuries Oct 31 '12 at 19:29

4 Answers4

5

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;
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
4
  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;
}
Mark B
  • 95,107
  • 10
  • 109
  • 188
4

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.

Ben Jackson
  • 90,079
  • 9
  • 98
  • 150
  • Part of my effort was redundancy reduction so this explains a lot. My misguided version of reducing return points. – Histuries Oct 31 '12 at 18:52
  • 2
    Note that whether branch prediction will kick in depends on how often the branch is hit, and that in some architectures there is a fixed bias (intel has a bias towards the 'if' side by default). You can hint the compiler using intrinsics that a branch is likely-unlikely to overcome that bias (the compiler will do whatever the platform requires, including reordering the code if that might yield better performance). Finally, write maintainable code, and only worry about performance and microoptimizations if your profiling shows that you do have a bottleneck there. – David Rodríguez - dribeas Oct 31 '12 at 19:15
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.

Community
  • 1
  • 1
πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190