-2

There is a little code example here:

struct Data {
};

struct Init {
    Data *m_data;

    Init() : m_data(new Data) { }
    ~Init() {
        delete m_data;
    }
};

class Object {
    private:
        int m_initType;
        Data *m_data;
    public:
        Object(const Init &init) : m_initType(0), m_data(init.m_data) { }
        Object(Init &&init) : m_initType(1), m_data(init.m_data) { init.m_data = nullptr; }
        ~Object() {
            if (m_initType==1) {
                delete m_data;
            }
        }
};

Object can be initialized two ways:

  • const Init &: this initialization just stores m_data as a pointer, m_data is not owned, so ~Object() doesn't have to do anything (in this case, m_data will be destroyed at ~Init())
  • Init &&: this initialization transfers ownership of m_data, Object becomes the owner of m_data, so ~Object() needs to destroy it

Now, there is a function:

void somefunction(Object object);

This function is called in callInitA and callInitB:

void callInitA() {
    Init x;
    somefunction(x); // calls the "const Init &" constructor
}

void callInitB() {
    somefunction(Init()); // calls the "Init &&" constructor
}

Now, here's what I'd like to accomplish: in the callInitA case, I'd like to make the compiler to optimize away the destructor call of the resulting temporary Object (Object is used frequently, and I'd like to decrease code size).

However, the compiler doesn't optimize it away (tested with GCC and clang).

Object is designed so it doesn't have any functions which alter m_initType, so the compiler would be able to find out that if m_initType is set to 0 at construct time, then it won't change, so at the destructor it is still be 0 -> no need to call destructor at all, as it would do nothing.

Even, m_initType is an unnecessary member of Object: it is only needed at destruct time.

Do you have any design ideas how to accomplish this?

UPDATE: I mean that using some kind of c++ construct (helper class, etc.). C++ is a powerful language, maybe with some kind of c++ trickery this can be done.

(My original problem is more complex that this simplified one: Object can be initialized with other kind of Init structures, but all Objects constructors boils down to getting a "Data*" somehow)

geza
  • 28,403
  • 6
  • 61
  • 135
  • 4
    You've profiled and found the presence of a destructor to be a bottleneck? – Chad Jun 16 '17 at 21:05
  • 2
    Why the manual memory management? Why not just make `m_data` a `std::unique_ptr` and then make the destructors `= default;`? – Jesper Juhl Jun 16 '17 at 21:05
  • 1
    Do you really need `Object(Init &&init)` as without it, you have also correct destruction. – Jarod42 Jun 16 '17 at 21:05
  • 1
    [The assembly](https://godbolt.org/g/wbRbna) generated from your code shows that GCC can and will avoid doing the `if (m_initType==1)` check when it can prove that it doesn't need to do it. That's entirely up to the compiler and the optimizations it performs though. – Miles Budnek Jun 16 '17 at 21:17
  • This is library code. This code complies into >10000 places. So actually, without profiling it, I know that this extra code slows down code a little bit. And profiling this kind of thing is not easy, if not impossible. If I find a solution for this problem, then I can profile it: how much time I gained. – geza Jun 16 '17 at 21:18
  • Miles, yes, it is optimized away because the compiler sees inside of somefunction, so it can prove that m_initType doesn't change. If you put somefunction into another compilation unit, it won't be optimized away unfortunately – geza Jun 16 '17 at 21:20
  • Jarod42, yes, I need it, because I want to be able to transfer ownership to Object. And maybe, Object propagates ownership to other object afterwards. – geza Jun 16 '17 at 21:25
  • @geza You'll need to turn on link-time optimization if you want things to be optimized across compilation units. – Miles Budnek Jun 16 '17 at 21:50
  • Thanks Miles, yes, it's an option. I'd like to avoid that. I'd like to use some kind of C++ construct which can accomplish this. C++ is a powerful language, maybe with some kind of trickery, this can be done. Using some helper classes, etc. I'm thinking about this problem for about a week, but I have found no solutions. – geza Jun 16 '17 at 22:02

2 Answers2

2
void callInitA() {
    Init x;
    somefunction(x); // calls the "const Init &" constructor
}

The destruction of x cannot be optimized away, regardless of the contents of Init. Doing so would violate the design of the language.

It's not just a matter of whether Init contains resources or not. Init x, like all objects, will allocate space on the stack that later needs to be cleaned up, as an implicit (not part of code that you yourself write) part of the destructor. It's impossible to avoid.

If the intention is for x to be an object that somefunction can call without having to repeatedly create and delete references to x, you should be handling it like this:

void callInitA(Init & x) { //Or Init const& x
    somefunction(x); // calls the "const Init &" constructor
}

A few other notes:

  • Make sure you implement the Rule of Five (sometimes known as Rule of Three) on any object that owns resources.
  • You might consider wrapping all pointers inside std::unique_ptr, as it doesn't seem like you need functionality beyond what std::unique_ptr offers.
Xirema
  • 19,889
  • 4
  • 32
  • 68
  • I'd like to optimize away ~Object(), not ~Init(). Thanks for the notes. This is a stripped down example, so it can be understood more easily. – geza Jun 16 '17 at 21:14
  • @geza I focused on the wrong code, but it's the same basic premise: there's no way to optimize away destructors. Destructors can have their internal workings optimized and refactored by the compiler if the compiler sees ways to improve performance, but the tasks performed by the Destructor cannot be optimized out. – Xirema Jun 16 '17 at 21:16
  • Xirema: in this case, it can be optimized. Look at what Miles [linked](https://godbolt.org/g/wbRbna): it is optimized away, if the compiler sees inside somefunction. I'd like to have a solution which works without it. – geza Jun 16 '17 at 21:29
0

Your m_initType actually distinguishes between two kinds of Objects - those which own their memory and those which don't. Also, you mention that actually there are many kinds of Objects which can be initialized with all sorts of inputs; so actually there are all sorts of Objects. That would suggest Object should better be some abstract base class. Now, that wouldn't speed anything up or avoid destructor calls, but it might make your design more reasonable. Or maybe Object could be an std::variant (new in C++17, you can read up on it).

But then, you say that temporary Objects are "used frequently". So perhaps you should go another way: In your example, suppose you had

template <bool Owning> class Object;

which you would then specialize for the non-owning case, with only a const Init& constructor and default destruction, and the owning case, with only an Init&& constructor (considering the two you mentioned) and a destructor which deletes. This would mean templatizing the code that uses Object, which many mean larger code size, as well as having to know what kind of Objects you pass in; but if would avoid the condition check if that really bugs you so much.

I'd like to decrease code size

I kind of doubt that you do. Are you writing code for an embedded system? In that case it's kind of strange you use lots of temporary Objects which are sort-of polymorphic.

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • In my real code, Object is 2-word sized (so it is small). Putting this object into stack can be done in two (or on ARM, one!) asm instructions. But calling the destructor is much more (if it's inlined) - so it will definitely decrease code size. It is a "little bit" polymorphic, yes (3 subtypes exist). Object cannot be a template, it would ruin its purpose (there are functions which have more Object parameters. Then these functions should be templates too). The main purpose of Object is that it can provide a Data object from 3 different kind of sources. – geza Jun 16 '17 at 22:51