Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

This is fun! The C version;

    int foo(int v) {
        // ...
        int something = 0;
        switch (v) {
            case FIRST_CASE:  something = 2; goto common1;
            case SECOND_CASE: something = 7; goto common1;
            case THIRD_CASE:  something = 9; goto common1;
            common1:
                /* code common to FIRST, SECOND and THIRD cases */
                break;

            case FOURTH_CASE: something = 10; goto common2;
            case FIFTH_CASE:  something = 42; goto common2;
            common2:
                /* code common to FOURTH and FIFTH cases */
                break;
        }
    }
The D version:

    int foo(int v) {
        // ...
        int something = 0;

        void common1() {
                /* code common to FIRST, SECOND and THIRD cases */
        }

        void common2() {
                /* code common to FOURTH and FIFTH cases */
        }

        switch (v) {
            case FIRST_CASE:  something = 2;  common1(); break;
            case SECOND_CASE: something = 7;  common1(); break;
            case THIRD_CASE:  something = 9;  common1(); break;
            case FOURTH_CASE: something = 10; common2(); break;
            case FIFTH_CASE:  something = 42; common2(); break;
            default: break;
        }
    }
Note the use of nested functions to factor out common code. The nested functions usually get inlined by the compiler, so there is no cost to them. Nested functions are a great way to eliminate gotos without penalty.


> The nested functions usually get inlined by the compiler, so there is no cost to them.

This kind of code typically gets written when ‘usually’ isn’t good enough (of course, once you use a compiler, in theory, there are no guarantees; the compiler could compile the inlined-function one with a goto or vice versa, but programmers typically are more concerned about what happens in practice)

The inlined functions also may increase code size and instruction cache pressure.

On the other hand, having a branch less may be beneficial.


I agree that programmers care more about what actually happens (and they should, when performance matters!), but this kind of analysis also involves future changes to the compiler unless it's a one-time job. Which sometimes exists, so check the assembly there and do whatever you need.

Straightforward and limited-scope code like nested functions tends to improve in performance over time, because it restricts possibilities better than goto. And it's more error-resistant to future changes for similar reasons. If your code has to last a while, you're probably better off having the safer one. Or maintain both, and use the safer one to validate the unsafe one, and choose based on benchmarks of the week - what was true when it was written could change with any version.


When it isn't good enough, inserting `pragma(inline, true)` will force it.

> The inlined functions also may increase code size and instruction cache pressure.

tail merging optimization takes care of that


gcc supports nested functions. I like them lots.

They seem to generate a lot of hate though.


I've never used them, so have no opinion on that.


Only some goto use cases.


Did I say otherwise? D still has a goto, though it is rarely used anymore.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: