Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

I think I can understand §5.3.4/11 in N4140, but the notion of an allocation function which has been extended is incomprehensible to me

The two paragraphs below were copied from N4140 (emphasis are mine).

§5.3.4/11:

When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array. For arrays of char and unsigned char, the difference between the result of the new-expression and the address returned by the allocation function shall be an integral multiple of the strictest fundamental alignment requirement (3.11) of any object type whose size is no greater than the size of the array being created. [ Note: Because allocation functions are assumed to return pointers to storage that is appropriately aligned for objects of any type with fundamental alignment, this constraint on array allocation overhead permits the common idiom of allocating character arrays into which objects of other types will later be placed. —end note ]

§5.4.3/12

When a new-expression calls an allocation function and that allocation has been extended, the size argument to the allocation call shall be no greater than the sum of the sizes for the omitted calls as specified above, plus the size for the extended call had it not been extended, plus any padding necessary to align the allocated objects within the allocated memory.

I can understand §5.3.4/11, but the notion of an allocation function which has been extended is incomprehensible to me, as stated above in §5.4.3/12.

like image 287
Belloc Avatar asked Oct 07 '15 11:10

Belloc


1 Answers

This language was added by N3664: Clarifying Memory Allocation which allows the implementation to fuse allocations when the allocations are not part of the observable behavior. In other words the allocation can be extended.

The function call itself is not extended but the function call and hence the allocation may be omitted and the allocations may be lumped together into a larger allocation later on.

The proposal states the problem (emphasis mine):

As strict reading of the current C and C++ standards may lead one to conclude that the allocation strategy shall not consider any information not derivable from the sequence of new and delete expressions. In essence, the standards may exclude macro-optimization of allocation.

On the other hand, a strict reading of the standards may lead one to conclude that the implementation must make an allocation function call for each and every new expression. This reading may exclude micro-optimization of allocation.

and proposes:

We propose to replace existing mechanistic wording with wording more precisely focused on essential requirements. The intent is to enable behavior that some existing compilers and memory allocators already have. For example, see TCMalloc

and further says:

An essential requirement on implementations is that they deliver usable memory, not that they have a particular sequence of allocation calls. We propose to relax the allocation calls with respect to new expressions.

  1. Within certain constraints, the number of allocation calls is not part of the observable behavior of the program. This enables implementations to reduce the number of allocation calls by avoiding them or fusing them.

  2. When avoiding or fusing allocations, the amount of space requested does not exceed that implied by the new expressions, with the exception of additional padding to meet alignment constraints. This means that the amount of space allocated does not increase peak allocation.

Because C++ class-specific memory allocators are often tuned to specific class sizes, we do not apply this relaxation to those allocators.

This can lead to allocations being completely omitted which can be surprising in some cases. We can see for the following example taken from this question:

#include <new>  

int main()
{
    int* mem = new (std::nothrow) int[100];
    return 0;
}

clang optimizes this to (see it live via godbolt):

main:                                   # @main
    xorl    %eax, %eax
    retq

This is allowable because no observable behavior is effected, so this falls under the umbrella of the as-if rule.

like image 97
Shafik Yaghmour Avatar answered Oct 11 '22 13:10

Shafik Yaghmour