views:

218

answers:

9

If I have a functor class with no state, but I create it from the heap with new, are typical compilers smart enough to optimize away the creation overhead entirely?

This question has come up when making a bunch of stateless functors. If they're allocated on the stack, does their 0 state class body mean that the stack really isn't changed at all? It seems it must in case you later take an address of the functor instance. Same for heap allocation.

In that case, functors are always adding a (trivial, but non-zero) overhead in their creation. But maybe compilers can see whether the address is used and if not it can eliminate that stack allocation. (Or, can it even eliminate a heap allocation?)

But how about a functor that's created as a temporary?

#include <iostream>

struct GTfunctor 
{
  inline bool operator()(int a, int b) {return a>b; }
};

int main()
{
  GTfunctor* f= new GTfunctor;
  GTfunctor g;

  std::cout<< (*f)(2,1) << std::endl;
  std::cout<< g(2,1) << std::endl;
  std::cout<< GTfunctor()(2,1) << std::endl;
  delete f;
}

So in the concrete example above, the three lines each call the same functor in three different ways. In this example, is there any efficiency difference between the ways? Or is the compiler able to optimize each line all the way down to being a compute-less print statement?

Edit: Most answers say that the compiler could never inline/eliminate the heap allocated functor. But is this really true as well? Most compilers (GCC, MS, Intel) have linktime optimization as well which could indeed do this optimization. (but does it?)

+3  A: 

are typical compilers smart enough to optimize away the creation overhead entirely?

When you're creating them on the heap, I doubt whether the compiler is allowed to. IMO:

  • Invoking new implies invoking operator new.
  • operator new is a non-trivial function defined in the run-time library.
  • The compiler isn't allowed to decide that you didn't really mean to invoke such a function and to decide that as an optimization it will silently not invoke it.

When you're creating them on the stack, and not taking their address, then maybe ... or maybe not: my guess is that every object has a non-zero size, in order to occupy some memory, in order to have an identity, even when the object has no state apart from its identity.

ChrisW
+1  A: 

I highly doubt this type of optimization is allowed, but if your functor has no state, why would you want to initialize it on the heap? It should be just as easy to use it as a temporary.

rlbond
It might be useful to allocate on the heap if you're dealing with many functors and want to treat them all the same way. One of the interesting questions is how expensive is that redundant heap allocation, and whether its useless nature is recognized by the compiler and its overhead allowed to be eliminated.
SPWorley
"if you're dealing with many functors and want to treat them all the same way"Do you mean you're using the functors polymorphically? With virtual function? If so, maybe the optimization is to do static polymorphism. With static polymorphism, you can usually trade executable image size with more speed.
Shing Yip
A: 

The compiler can probably figure out that operator() doesn't use any member variables, and optimize it to the max. I wouldn't make any assumptions about the local or heap allocated variables, though.

Edit: When in doubt, turn on the assembly output option on your compiler and see what it's actually doing. No sense in listening to a bunch of idiots on the web when you can see the real answer for yourself.

Mark Ransom
+1  A: 

A C++ object is always non-zero in size. "Empty base class optimization" allows empty base class to have zero size but that doesn't apply here.

I have not worked on any C++ optimizer, so whatever i say is just speculating. I think 2nd and 3rd will be expanded inline easily and there will be no overhead, and no GTFunctor is created. The functor pointer, however, is a different story. In your example, it may seem simple enough and any optimizer should be able to eliminate heap allocation, but in a non trivial program, you maybe creating the functors in one translation unit and use it in another. Or even in a different library where the compiler/linker/loader/runtime system don't have source code to, and it is almost impossible to optimize. Given the fact that optimizing it is not easy, the potential gain in performance is not great, and the number of cases where empty functor is allocated in the heap is probably small, i think most optimizer programmer will probably not put this optimization high in their to do list.

Shing Yip
Indeed, all objects are non-zero size, but only if the compiler actually constructs them. In the code example, the third call makes a temporary object and the optimizer will almost certainly inline its function call and never actually create the object to begin with. It could do the same to the stack and heap examples, but does it, and if not, is it because it's illegal or because it's too difficult to determine even in this simple example?
SPWorley
I think the stack object can be easily optimized away since the compiler generate code to allocate and deallocate auto objects so it has full control. Heap object is different because it is the runtime library who does the new/delete. It is not easy for the compiler to know if new/delete has side effects other than constructor/allocate and call destructor/deallocate. To make matters worse operator new and operator delete can be overridden.
Shing Yip
+1  A: 

The compiler cannot optimize out a call to new or delete. It may however optimize out the variable created on the stack since it has no state.

Stephen Nutt
+3  A: 

Obviously, it depends on your compiler.

I would say

  • No compiler will optimize away the object on the heap. (This is because, as ChrisW says, compilers will never optimize away a call to new, which is almost certainly defined in another translation unit.)

  • Some compilers will optimize away a named object on the stack. I've known gcc to do this optimization quite often.

  • Most compilers will optimize away an unnamed object on the stack. This is one of the "standard" C++ optimizations, especially as more advanced C++ users tend to create lots of unnamed temporary variables.

Unfortunately, these are only rules of thumb. Optimizers are notoriously unpredictable; really the only way to know what your compiler is doing is to read the assembly output.

Charlie Tangora
A: 

The answer to your question has two aspects.

  1. Does the compiler optimize away the heap allocation: I strongly doubt it, but I'm not a standard guy, so I have to look it up.

  2. Can the compiler optimize by inline the object's operator()? Yes. As long as you don't specify the call as virtual, even the pointer dereferencing isn't actually performed.

xtofl
+1  A: 

Simple way to answer the heap question:

GTfunctor *f = new GTfunctor;

The value of f must not be null, so what should it be? And you also had:

GTfunctor *g = new GTfunctor;

Now the value of g must not equal the value of f, so what should each be?

Furthermore, neither f or g may be equal to any other pointer obtained from new, unless some pointer elsewhere is somehow initialised to be equal to f or g, which (depending on the code that comes after) may involve examining what the entire rest of the program does.

Yes, if by local examination of the code the compiler can see that you never rely on any of these requirements, then it could perform a rewrite such that no heap allocation occurs. The problem is, if your code was that simple, you could probably do that rewrite yourself and end up with a more readable program anyway, e.g. your test program would look like your stack-based g example. So real programs would not benefit from such an optimisation in the compiler.

Presumably the reason you're doing this is because sometimes the functor does have data, depending on which type is chosen at runtime. So compile-time analysis cannot usefully work its magic here.

Daniel Earwicker
+1  A: 

C++ Standard states that each object (imho on the heap) must at least have a size one byte, so it can be uniquely addressed.

Generating functors with new can lead to two problems:

  1. The constructions can generally not optimized away. New is a function with complex side effects (bad_alloc).
  2. Because you address the functor indirectly the compiler may not be able to inline the function.

Chances are good that you will not see a sign of the functor, if you generate it on the stack.

Side note: The inline statement is not necessary. Every function which is defined in a class definition is treated as inlineable.

ebo