views:

101

answers:

6

Lets say you have some functions in some classes are called together like this

myclass::render(int offset_x, int offset_y)
{
    otherClass.render(offset_x, offset_y)
}

This pattern will repeat for a while possibly through 10+ classes, so my question is:

Are modern C++ compilers smart enough to recognise that wherever the program stores function parameters - From what wikipedia tells me it seems to vary depending on parameter size, but that for a 2 parameter function the processor register seems likely - doesn't need to be overridden with new values?

If not I might need to look at implementing my own methods

+1  A: 

I'm not a specialist, but it looks a lot like the perfect forwarding problem that will be solved in the next standard (C++0x) by using rvalue-references.

Currently I'd say it depend on the compiler, but I guess if the function and the parametters are simple enough then yes the function will serve as a shortcut. If this function is imlpemented directly in the class definition (and then becoming implicitely candidate for inlining) it might be inligned, making the call directly call the wanted function instead of having two runtime calls.

Klaim
Perfect forwarding is about `const` correctness in templates, not compiler optimizations of registers.
Billy ONeal
A: 

If I understand the question correctly, you are asking "Are most compilers smart enough to inline a simple function like this", and the answer to that question is yes. Note however the implicit this paremeter which is part of your function (because your function is part of a class), so it might not be completely inlineable if the call level is deep enough.

Billy ONeal
My question wasn't about inlining.
Tomas Cokis
+3  A: 

I think it's more likely that the compiler will make a larger-scale optimization. You'd have to examine the actual machine code produced, but for example the following trivial attempt:

#include <iostream>

class B {
public:
    void F( int x, int y ) {
        std::cout << x << ", " << y << std::endl; 
    }
};

class A {
    B b;

public:
    void F( int x, int y ) {
        b.F( x, y );
    }
};

int main() {
        A a;
        a.F( 32, 64 );
}

causes the compiler (cl.exe from VS 2010, empty project, vanilla 'Release' configuration) to produce assembly that completely inlines the call tree; you basically get "push 40h, push 20h, call std::operator<<."

Abusing __declspec(noinline) causes cl.exe to realize that A::F just forwards to B::F and the definition of A::F is nothing but "call A::F" without stack or register manipulation at all (so in that case, it has performed the optimization you're asking about). But do note that my example is extremely contrived and so says nothing about the compiler's ability to do this well in general, only that it can be done.

In your real-world scenario, you'll have to examine the disassembly yourself. In particular, the 'this' parameter needs to be accounted for (cl.exe usually passes it via the ECX register) -- if you do any manipulation of the class member variables that may impact the results.

Josh Petrie
Thanks for actually testing it out!
Tomas Cokis
In the code, both `B::F()` and `A::F()` has inline (inside the class) definitions. Would the statements hold if they were defined outside the class too?
ArunSaha
Modern compilers are pretty good at selecting candidates for inlining; while it's true that defining them outside the class definitions would mean they were not implicitly declared inline, most compilers would still inline them (cl.exe does). That's why I forcibly prevented it with __declspec(noinline).
Josh Petrie
Getting a haircut to lose weight? Basically *all* the time goes into `cout` when it processes `endl`.
Mike Dunlavey
Yes (because std::endl causes a stream flush). I'm not sure what your point is though? The issue we're investigating isn't actual execution time, per se, it's what the compiler does with the code.
Josh Petrie
Yeah, I suppose it's just an academic question. Whenever I worked in generating assembly language, I assumed work put into optimization was only worth it in certain circumstances. The rest of the time nobody would ever notice.
Mike Dunlavey
I still sleep better at night knowing that whatever implementation I used was efficient
Tomas Cokis
A: 

The problem with inlining is that the compiler will probably only be able to do this for a given compilation unit. The linker is probably less likely to be clever enough to inline from one compilation unit to another.

But given the total trivial nature of the function and that both functions have exactly the same arguments in the same order, the cost of the function call will probably be only one machine instruction viz. an additional branch (or jump) to the true implementation. There is no need to even push the return address onto the stack.

doron
+2  A: 

Yes, it is. The compiler performs dataflow analysis before register allocation, keeping track of which data is where at which time. And it will see that the arg0 location contains the value that needs to be in the arg0 location in order to call the next function, and so it doesn't need to move the data around.

jalf
+1  A: 

In spite of your comment, I think that inlining is germane to this discussion. I don't believe that C++ compilers will do what you're asking (reuse parameters on the stack) UNLESS it also inlines the method completely.

The reason is that if it's making a real function call it still has to put the return address onto the stack, thus making the previous call's parameters no longer at the expected place on the stack. Thus in turn is has to put the parameters back on the stack a second time.

However I really wouldn't worry about that. Unless you're making a ridiculous number of function calls like this AND profiling shows that it's spending a large proportion of its time on these calls they're probably extremely minimal overhead and you shouldn't worry about it. For a function that small however, mark it inline and let the compiler decide if it can inline it away completely.

Mark B
Then the answer I was after was "No, they are not"
Tomas Cokis