views:

130

answers:

3

Assuming MyClass uses the default destructor (or no destructor), and this code:

MyClass *buffer = new MyClass[i];
// Construct N objects using placement new
for(size_t i = 0; i < N; i++){
    buffer[i].~MyClass();
}
delete[] buffer;

Is there any optimizer that would be able to remove this loop?

Also, is there any way for my code to detect if MyClass is using an empty/default constructor?

EDIT: Sorry about my horrible code. I think this is correct now..

+3  A: 

The correct syntax for calling a destructor is

template<typename T>
void destruct(T& x)
{
    x.~T();  // call destructor on x
}

// The below is valid even though ints do not have destructors
int x;
destruct(x);

That syntax is valid for types like int (when passed as a template parameter) but is a no-op (does nothing), so template code such as that in std::vector<T> which calls destructors on its contents, is valid.

IMO it should be straightforward for the compiler to see that the loop contents contains a no-op, therefore the entire loop itself has no side effects, therefore remove the entire loop. Modern compilers have very sophisticated optimisers and should be more than capable of removing code which has no effect. If a compiler didn't remove redundant loops, it would emit redundant code in the destructor of a vector<int>! There is no code to emit for the destructor of an int, so there would just be an empty loop iterating through the elements doing nothing. I'm sure any sane optimiser would remove that entire loop.

Of course, if you're calling the destructor on a class which does do work in its destructor, that must still be called and there will still be a loop (subject to other related optimisations such as unrolling).

Another simple example of optimisation based on side-effects is code like this:

for (int i = 0; i < 1000000; ++i)
    ;  // just count up i, no statement (same as no-op)

cout << i;

will probably be optimised to simply print the constant 1000000 with no processing, because the compiler is smart enough to know the overall side effect is i becomes a million and gets printed. This is the very basics for some of the impressive things optimisers do, so don't worry about it, it's going to do an excellent job. If you're ever curious, examine the output assembly in an optimised build to see what's really going on.

AshleysBrain
`x.~int(); // valid even though an int!` I don't think that's valid.
GMan
You're right, I can't get the statement x.~int() to compile directly. But it does compile through a template function. I've changed the example to demonstrate that.
AshleysBrain
A: 

You don't create a dynamic array as you have done above. You do it as follows:

MyClass* buffer = new MyClass[i];

That aside the loop you have above doesn't call the destructor either. If the class has an overloaded "~" operator then it will call that code.

So no ... no compiler will optimise that loop out. The code is highly unlikely to compile as well.

Goz
+3  A: 

There are a few things wrong with this code.

Firstly, you don't need to be calling the destructor. MyClass buffer* = new MyClass[i]; delete[] buffer; does that just fine. (Note, not the array syntax.)

That said, you comment leads me to believe you meant something else, like:

// vector, because raw memory allocation is bad
std::vector<char> memory(sizeof(MyClass) * count); 

std::vector<MyClass*> objs; objs.reserve(count);
for (size_t i = 0; i < count; ++i)
    objs.push_back(new (memory[sizeof(MyClass) * i]) MyClass()); // place it

Then later:

for (size_t i = 0; i < count; ++i)
    objs[i].~MyClass(); // destruct (note syntax)

Of course there's no need to delete anything, since we used a vector. This is the correct syntax for calling a destructor.

Will it be optimized? It depends of the compiler can determine if the destructor does nothing. If the destructor is compiler-generated, I'm sure it'll remove the worthless loop. If the destructor is user-defined but in the header, it'll also be able to see it does nothing and remove the loop.

However, if it's in some other object file, I don't think it will, even if it's empty. That depends on your compilers ability to optimize during the linking phase. The best way to know is to look at the generated assembly.

GMan
Yes, you're right about what I was meaning to do. I didn't try to compiling this before posting it :\ So what it sounds like is that it will optimize it out, as long as it can tell that it's safe to do so. I assume that means it would be better to not define a destructor, since that was it can just look in the header and know that doesn't do anything?
Brendan Long
Also to clarify, the destructor won't be deleting anything, I'm mostly wondering about pool allocators (where you don't need to do any memory management, but you might still want the destructors called).
Brendan Long
@Brendan: Right. It's never a good idea to define things if you don't need to, it's just a waste of time and more work for the compiler.
GMan
@Brendan: if you _know_ in advance that the destructor will not contain anything, then why not simply not call it in your memory pool code? I assume that you want this to be generic so it can be used with destructors and without... You can always test it: gcc -O3 -S ... and then have a look at the generated assembly
Dan
Yes, I plan to make it generic, and I think this is how Boost's object_pool works and I was wondering if there's a chance of it being optimized out in that.
Brendan Long