0

Let's say I have a function

bool inline fn(int a) {
    if (a == 0) {
        a = 1; //or some other computation
        return true;
    }
    return false;
}

int main() {
    int a = 0;

    if (fn(a)) {
        return 1;
    }
}

will the main code be roughly inlined to:

int a = 0;

bool test = false;
if (a == 0) {
    a = 1;   //or some other computation
    test = true;
}

if (test) {
    return 1;
}

thus resulting in two ifs, OR rather it would look more like this:

int a = 0;

if (a == 0) {
    a = 1;   //or some other computation
    return 1;
}

which I obviously wanted to achieve. I'm using functions here not to make the executable smaller or anything, but purely to make the code more readable.

Actually why I do this is in the next example – imagine the function fn is templated, so that I can choose of more implementations of the function, while having the caller function exhibiting common behavior to all it's template instances, delegating the specific functionality to called functions.

Again this usage is purely for code reuse and readability. The functions will called/inlined in a single place in the code (that is the base_function).

I want to know, if tests on return values of the functions are efficiently optimized, so this code reuse technique doesn't interfere with performace/ actual execution at all.

template<typename TagSwitch, typename ... Args>
void base_function(Args ... args) {
    // some base behavior meant to be common to all functions "derived" from this function

    if (do_end(TagSwitch(), args ...)) {
        return;
    }

    //specific_behavior(TagSwitch(), args ...);
}

// default for all specific ("derived") functions is don't end
template<typename TagSwitch, typename ... Args>
bool inline do_end(TagSwitch, Args ... args) {

    return false;
}

// here I define my specific ("derived") function

struct MySpecificFunctionTag {};

template<typename ... Args>
bool inline do_end(MySpecificFunctionTag, int a, Args ... args) {
    if (a == 0) {
        //Do something (set a parameter)
        return true;
    }

    return false;
}

int main() {
    base_function<MySpecificFunctionTag>(1); 
}

I'd like to know, if the test if (do_end(TagSwitch(), args ...)) { in base_function<MySpecificFunctionTag>(1) instantiation would result in two ifs or one would be optimized out.

Adam
  • 1,724
  • 4
  • 21
  • 31
  • 1
    if the first test fails the second `if` wont be reached, so basically the second condition is always `true`. unlikely that branch prediction wont detect that pattern (irrespective of what your compiler will do to the code) – 463035818_is_not_an_ai Sep 28 '18 at 12:33
  • 1
    The best (often only) way to answer the question "will my compiler optimize this the way I expect it to" is to look at what the compiler actually does: https://godbolt.org/z/LLgvIo (Generally, you shouldn't be asking that question at all though, unless you've profiled your code and identified this particular bit as a problem.) – You Sep 28 '18 at 12:36
  • Don't use `inline` unless you know exactly what you're doing. It is from a time where compilers didn't do much optimization so the programmer had to do it himself. Nowadays, compilers are much better at optimizing and will figure out on their own whether they should inline or not. Just leave inlining to them - they'll do the right thing. – Blaze Sep 28 '18 at 12:44
  • 4
    @Blaze: Rather, don't expect `inline` to force inlining (because that's not what it does; it does other very useful things related to the ODR rule). In this case, the `inline` is already implicit since `do_end` is a template (see e.g. [_Does it make any sense to use inline keyword with templates?_](https://stackoverflow.com/q/10535667/147845)). – You Sep 28 '18 at 12:48
  • 3
    @Blaze "Don't use `inline`" is misleading. There is no problem with declaring a method as inline. You can put `inline` all over the place, but the compiler will decide on his own what to actually inline or not. – 463035818_is_not_an_ai Sep 28 '18 at 12:54
  • @user463035818 compiler decides whether to inline indeed, but it would be relevant to mention that the compiler cannot decide to expand inline unless it can do so, and the compiler cannot expand calls to functions that are defined in another translation unit than where the call is made. Declaring the function inline (either explicitly or implicitly) is what allows and mandates the function to be defined in all translation units, and therefore allows it to be expanded inline regardless of where the call is made. Link time optimization expands the possibilities but is not always an option. – eerorika Sep 28 '18 at 13:16
  • @user2079303 not sure why you adress that at me... I was refering to "Don't use inline unless you know exactly what you're doing" which I consider as misleading (if not wrong), because even if you dont know what you are doing, an `inline` too much wont hurt anybody, because anyhow the compiler does decide what to inline or not rather independent of whether you declared methods inline. – 463035818_is_not_an_ai Sep 28 '18 at 13:19
  • @user463035818 I addressed you because you stated `the compiler will decide on his own what to actually inline` which is an overly simplified view and that perception is probably a reason why Blaze thinks that you shouldn't use `inline`. More accurate: You should declare functions inline when you prefer them to be expanded inline, but you must not assume that they will be. `inline too much wont hurt anybody` is also overly simplified. `inline` functions have a cost: They must be compiled again and again for every TU they are used in. Only use `inline` if you are prepared to pay the price. – eerorika Sep 28 '18 at 13:28
  • In modern C++, `inline` is a way to put non-static, non-class-member functions into headers, without violating ODR. It has no other uses, pretty much., but for this particular use it’s indispensable. I recently converted a large codebases from “utility classes” having short methods in headers, to utility namespaces having short free functions in headers – and `inline` is the way to do it. – Kuba hasn't forgotten Monica Sep 28 '18 at 13:34

1 Answers1

1

Is testing for a return value of an inline (templated) function, which is itself testing, optimized to one test?

It can be optimized to one test, yes. In fact, both of your tests can be optimized away, since the value of the tested expression can be known at compile time. The entire program can be optimized to:

main:
        mov     eax, 1
        ret

I.e. Always return 1, and do nothing else. The latter, template example returns 0 instead, but is otherwise identical.


The test cannot be optimized away from the function fn, and the check after the function call cannot be optimized away unless the return value of the function can be known at compile time. So, a prerequisite for merging the tests to one is that the optimizer must be able to expand the call inline.

eerorika
  • 232,697
  • 12
  • 197
  • 326