5

Let's say I have a class that's something like this:

class View
{
public:
    View(DataContainer &c)
        : _c(c)
    {
    }

    inline Elem getElemForCoords(double x, double y)
    {
        int idx = /* some computation here... */;
        return _c.data[idx];
    }

private:
    DataContainer& _c;
};

If I have a function using this class, is the compiler allowed to optimize it away entirely and just inline the data access?

Is the same still true if View::_c happens to be a std::shared_ptr?

Ali
  • 56,466
  • 29
  • 168
  • 265
Siegfried Gevatter
  • 3,624
  • 3
  • 18
  • 13
  • If the compiler inlines both the constructor and the function, there isn't really much of the class left. – Mysticial Apr 15 '14 at 19:54
  • @Mysticial: Yeah, I guess that makes sense. I wanted to be sure there are no weird corner cases or guarantees in the standard that limit what compilers can do here. – Siegfried Gevatter Apr 15 '14 at 19:57
  • 5
    Yes. In fact compilers may apply any transformations that doesn't change the observable behavior of the program, they can -- and do -- change function signatures, split functions, merge functions, remove functions, replace a function by its result and so on. More details here: http://stackoverflow.com/questions/15718262/what-exactly-is-the-as-if-rule – pentadecagon Apr 15 '14 at 20:05
  • 2
    And that "observable behavior" is only what is observable outside the program: not by using a debugger. The optimizer is allowed to change anything that is only observable with special tools. I only mention this because someone on a C# question thought the C# optimizer was breaking rules because some code wasn't allocating memory the way he thought it should be. – Zan Lynx Apr 15 '14 at 20:06

2 Answers2

7

If I have a function using this class, is the compiler allowed to optimize it away entirely and just inline the data access?

Is the same still true if View::_c happens to be a std::shared_ptr?

Absolutely, yes, and yes; as long as it doesn't violate the as-if rule (as already pointed out by Pentadecagon). Whether this optimization really happens is a much more interesting question; it is allowed by the standard. For this code:

#include <memory>
#include <vector>

template <class DataContainer>
class View {
public:
    View(DataContainer& c) : c(c) { }

    int getElemForCoords(double x, double y) {
        int idx = x*y; // some dumb computation
        return c->at(idx);
    }
private:
    DataContainer& c;
};

template <class DataContainer>
View<DataContainer> make_view(DataContainer& c) {
  return View<DataContainer>(c);
}

int main(int argc, char* argv[]) {

  auto ptr2vec = std::make_shared<std::vector<int>>(2);

  auto view = make_view(ptr2vec);

  return view.getElemForCoords(1, argc);
}

I have verified, by inspecting the assembly code (g++ -std=c++11 -O3 -S -fwhole-program optaway.cpp), that the View class is like it is not there, it adds zero overhead.


Some unsolicited advice.

  • Inspect the assembly code of your programs; you will learn a lot and start worrying about the right things. shared_ptr is a heavy-weight object (compared to, for example, unique_ptr), partly because of all that multi-threading machinery under the hood. If you look at the assembly code, you will worry much more about the overhead of the shared pointer and less about element access. ;)

  • The inline in your code is just noise, that function is implicitly inline anyway. Please don't trash your code with the inline keyword; the optimizer is free to treat it as whitespace anyway. Use link time optimization instead (-flto with gcc). GCC and Clang are surprisingly smart compilers and generate good code.

  • Profile your code instead of guessing and doing premature optimization. Perf is a great tool.

  • Want speed? Measure. (by Howard Hinnant)

Community
  • 1
  • 1
Ali
  • 56,466
  • 29
  • 168
  • 265
  • Thanks for the answer, Ali. Yeah, I didn't add it to the question but I was also wondering whether the shared_ptr can be optimized away if the caller already has one. I guess that'd be asking too much of the compiler :P. As for the inline keyword, I've got "duplicate definition" errors in the past when including code in headers (with guards) if I didn't explicitly declare them as inline. – Siegfried Gevatter Apr 15 '14 at 21:19
  • @SiegfriedGevatter I am glad the answer helped. You get duplicate definitions if you implement *ordinary* functions but not class member functions; those are implicitly inline. Templates are implicitly inline as well. – Ali Apr 15 '14 at 21:21
  • I see, I'll have to try it out again. – Siegfried Gevatter Apr 15 '14 at 21:35
1

In general, compilers don't optimize away classes. Usually, they optimize functions.

The compiler may decide to take the content of simple inlined functions and paste the content where the function is invoked, rather than making the inlined function a hard-coded function (i.e. it would have an address). This optimization depends on the compiler's optimization level.

The compiler and linker may decide to drop functions that are not used, whether they be class methods or free standing.

Think of the class as a stencil for describing an object. The stencil isn't any good without an instance. An exception is a public static function within the class (static methods don't require object instances). The class is usually kept in the compiler's dictionary.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
  • Clarification: a class by itself doesn't generate any machine code, so there is nothing for a compiler to optimize. – Oktalist Apr 15 '14 at 20:24
  • @Oktalist: Please clarify how a compiler handles a class with a public static member function, and also a class with only functions. – Thomas Matthews Apr 15 '14 at 20:41
  • I think your answer already explained that adequately. The code in the member functions can be optimized, but not the class itself. I was attempting the clarify the reason behind this. – Oktalist Apr 16 '14 at 19:09