Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can the compiler optimize from heap to stack allocation?

As far as compiler optimizations go, is it legal and/or possible to change a heap allocation to a stack allocation? Or would that break the as-if rule?

For example, say this is the original version of the code

{     Foo* f = new Foo();     f->do_something();     delete f; } 

Would a compiler be able to change this to the following

{     Foo f{};     f.do_something(); } 

I wouldn't think so, because that would have implications if the original version was relying on things like custom allocators. Does the standard say anything specifically about this?

like image 878
Cory Kramer Avatar asked Nov 02 '17 12:11

Cory Kramer


People also ask

How are compilers optimized?

Compiler optimization is generally implemented using a sequence of optimizing transformations, algorithms which take a program and transform it to produce a semantically equivalent output program that uses fewer resources or executes faster.

Are allocations faster on stack or heap?

Because the data is added and removed in a last-in-first-out manner, stack-based memory allocation is very simple and typically much faster than heap-based memory allocation (also known as dynamic memory allocation) typically allocated via malloc .

Why is stack allocation faster than heap?

The stack is faster because the access pattern makes it trivial to allocate and deallocate memory from it (a pointer/integer is simply incremented or decremented), while the heap has much more complex bookkeeping involved in an allocation or free.

Can you allocate memory on the stack?

We can allocate variable length space dynamically on stack memory by using function _alloca. This function allocates memory from the program stack. It simply takes number of bytes to be allocated and return void* to the allocated space just as malloc call.


2 Answers

Yes, it's legal. expr.new/10 of C++14:

An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.

expr.delete/7:

If the value of the operand of the delete-expression is not a null pointer value, then:

— If the allocation call for the new-expression for the object to be deleted was not omitted and the allocation was not extended (5.3.4), the delete-expression shall call a deallocation function (3.7.4.2). The value returned from the allocation call of the new-expression shall be passed as the first argument to the deallocation function.

— Otherwise, if the allocation was extended or was provided by extending the allocation of another new- expression, and the delete-expression for every other pointer value produced by a new-expression that had storage provided by the extended new-expression has been evaluated, the delete-expression shall call a deallocation function. The value returned from the allocation call of the extended new-expression shall be passed as the first argument to the deallocation function.

— Otherwise, the delete-expression will not call a deallocation function (3.7.4.2).

So, in summary, it's legal to replace new and delete with something implementation defined, like using the stack instead of heap.

Note: As Massimiliano Janes comments, the compiler could not stick exactly to this transformation for your sample, if do_something throws: the compiler should omit destructor call of f in this case (while your transformed sample does call the destructor in this case). But other than that, it is free to put f into the stack.

like image 137
geza Avatar answered Sep 19 '22 14:09

geza


These are not equivalent. f.do_something() might throw, in which case the first object remains in memory, the second gets destructed.

like image 45
lorro Avatar answered Sep 19 '22 14:09

lorro