Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > C++ > placement new overhead

Reply
Thread Tools

placement new overhead

 
 
Marc
Guest
Posts: n/a
 
      01-29-2011
Hello,

I have a memory region (I got it through std::aligned_storage), and I
initialize it using placement new. Looking at the generated code
(gcc-4.6), I notice that:
new(location) Type(arguments);
is implemented as:
if(location!=0) call the constructor

this causes quite a bit of overhead when Type is a trivial type (the
compiler can't always prove that location is not null).

I can't seem to find where the standard says that placement new can be
called with a null argument and should do nothing in that case. Is
this the case?
 
Reply With Quote
 
 
 
 
Öö Tiib
Guest
Posts: n/a
 
      01-29-2011
On Jan 29, 5:31*pm, Marc <marc.gli...@gmail.com> wrote:
> Hello,
>
> I have a memory region (I got it through std::aligned_storage), and I
> initialize it using placement new. Looking at the generated code
> (gcc-4.6), I notice that:
> new(location) Type(arguments);
> is implemented as:
> if(location!=0) call the constructor
>
> this causes quite a bit of overhead when Type is a trivial type (the
> compiler can't always prove that location is not null).


Can you provide some numbers? How many millions of placement news and
how large fraction of a second is that "quite a bit of overhead"?

> I can't seem to find where the standard says that placement new can be
> called with a null argument and should do nothing in that case. Is
> this the case?


What implementation should do on case of null? It can't construct at
null pointer. If it is undefined, then implementation may do what it
pleases. Popular choices are ignoring (not checking), doing nothing,
throwing or raising a signal.
 
Reply With Quote
 
 
 
 
Marc
Guest
Posts: n/a
 
      01-29-2011
Öö Tiib wrote:

> On Jan 29, 5:31*pm, Marc <marc.gli...@gmail.com> wrote:
>> Hello,
>>
>> I have a memory region (I got it through std::aligned_storage), and I
>> initialize it using placement new. Looking at the generated code
>> (gcc-4.6), I notice that:
>> new(location) Type(arguments);
>> is implemented as:
>> if(location!=0) call the constructor
>>
>> this causes quite a bit of overhead when Type is a trivial type (the
>> compiler can't always prove that location is not null).

>
> Can you provide some numbers? How many millions of placement news and
> how large fraction of a second is that "quite a bit of overhead"?


I haven't measured, but in trivial cases, new takes twice as much time
as it should. And I have applications where this constructor accounts
for roughly 10% of the running time.

In any case, I am also interested in the theoretical aspect.

>> I can't seem to find where the standard says that placement new can be
>> called with a null argument and should do nothing in that case. Is
>> this the case?

>
> What implementation should do on case of null? It can't construct at
> null pointer. If it is undefined,


That is what I am asking, whether it is undefined.

> then implementation may do what it
> pleases. Popular choices are ignoring (not checking), doing nothing,
> throwing or raising a signal.


When I write *i=x, this is also undefined if i==0, but I don't see the
compiler generating a comparison. I would expect the same here.
 
Reply With Quote
 
Andy Venikov
Guest
Posts: n/a
 
      02-15-2011
On 01/29/2011 01:55 PM, Marc wrote:
> Öö Tiib wrote:
>
>> On Jan 29, 5:31 pm, Marc<marc.gli...@gmail.com> wrote:
>>> Hello,
>>>
>>> I have a memory region (I got it through std::aligned_storage), and I
>>> initialize it using placement new. Looking at the generated code
>>> (gcc-4.6), I notice that:
>>> new(location) Type(arguments);
>>> is implemented as:
>>> if(location!=0) call the constructor
>>>
>>> this causes quite a bit of overhead when Type is a trivial type (the
>>> compiler can't always prove that location is not null).

>>
>> Can you provide some numbers? How many millions of placement news and
>> how large fraction of a second is that "quite a bit of overhead"?

>
> I haven't measured, but in trivial cases, new takes twice as much time
> as it should. And I have applications where this constructor accounts
> for roughly 10% of the running time.
>
> In any case, I am also interested in the theoretical aspect.
>
>>> I can't seem to find where the standard says that placement new can be
>>> called with a null argument and should do nothing in that case. Is
>>> this the case?

>>
>> What implementation should do on case of null? It can't construct at
>> null pointer. If it is undefined,

>
> That is what I am asking, whether it is undefined.
>
>> then implementation may do what it
>> pleases. Popular choices are ignoring (not checking), doing nothing,
>> throwing or raising a signal.

>
> When I write *i=x, this is also undefined if i==0, but I don't see the
> compiler generating a comparison. I would expect the same here.


What's the optimization option you're using?

And I do think it unspecified (as opposed to undefined which would be
the case if you passed the actual NULL to the placement new).
Unspecified means that the compiler is free to do whatever it's pleases
without invoking UB.

Andy.
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-16-2011
On Jan 29, 3:31 pm, Marc <marc.gli...@gmail.com> wrote:

> I have a memory region (I got it through std::aligned_storage), and I
> initialize it using placement new. Looking at the generated code
> (gcc-4.6), I notice that:
> new(location) Type(arguments);
> is implemented as:
> if(location!=0) call the constructor


> this causes quite a bit of overhead when Type is a trivial type (the
> compiler can't always prove that location is not null).


> I can't seem to find where the standard says that placement new can be
> called with a null argument and should do nothing in that case. Is
> this the case?


The signature of placement new is:
void* operator new( size_t, void* ) throw();
The "throw()" means that operator new can return a null pointer,
and that the generated code must behave correctly if it does.
(That's the general rule for all operator new functions: if they
have an empty exception specifier---they declare that they never
throw---then they report insufficient memory by returning a null
pointer.) From a language point of view, this means that the
compiler must test for null before calling the constructor.

With regards to this placement form of operator new in
particular, the standard doesn't say anything about the second
argument, *except* that it will be returned immediately. So
presumably, any pointer which would be valid as a return value
of this operator new would be a valid argument. And because of
the throw(), null is a valid return value.

--
James Kanze
 
Reply With Quote
 
Martin B.
Guest
Posts: n/a
 
      02-16-2011
On 16.02.2011 11:00, James Kanze wrote:
> On Jan 29, 3:31 pm, Marc<marc.gli...@gmail.com> wrote:
>
>> I have a memory region (I got it through std::aligned_storage), and I
>> initialize it using placement new. Looking at the generated code
>> (gcc-4.6), I notice that:
>> new(location) Type(arguments);
>> is implemented as:
>> if(location!=0) call the constructor

>
>> this causes quite a bit of overhead when Type is a trivial type (the
>> compiler can't always prove that location is not null).

>
>> I can't seem to find where the standard says that placement new can be
>> called with a null argument and should do nothing in that case. Is
>> this the case?

>
> The signature of placement new is:
> void* operator new( size_t, void* ) throw();
> The "throw()" means that operator new can return a null pointer,
> and that the generated code must behave correctly if it does.
> ...


Interesting conclusion. Could you explain how you arrive at this
conclusion from the std?

I'll quote N3092 (p 44:
- - -
18.6.1.3 Placement forms
1 (...)
void* operator new(std::size_t size, void* ptr) throw();
2 Returns: ptr.
3 Remarks: Intentionally performs no other action.
4 [ Example: This can be useful for constructing an
object at a known address:
void* place = operator new(sizeof(Something));
Something* p = new (place) Something();
—end example ]
- - -

cheers,
Martin
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      02-16-2011
On Feb 16, 2:03 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
> On 16.02.2011 11:00, James Kanze wrote:
>
> Interesting conclusion. Could you explain how you arrive at this
> conclusion from the std?
>
> I'll quote N3092 (p 44:
> - - -
> 18.6.1.3 Placement forms
> 1 (...)
> void* operator new(std::size_t size, void* ptr) throw();
> 2 Returns: ptr.
> 3 Remarks: Intentionally performs no other action.
> 4 [ Example: This can be useful for constructing an
> object at a known address:
> void* place = operator new(sizeof(Something));
> Something* p = new (place) Something();
> —end example ]


I think I can fill in the details on that.

Consider an expression like:
new( a1, a2, a3 ) T( b1, b2, b3 );

In order for the above to compile, there needs to be a function
declared of the form:
void* operator new( std::size_t, U1, U2, U3 ) noexcept; //or without
noexcept.
That can match the call expression "operator new( a1, a2, a3 )".

That new expression has to call that allocation function and compare
the return value to null, in order to decide whether to construct an
object or not. Generally, there's no way for the new expression to
know apriori that the return value isn't null.

See 5.3.4/11:
The new-placement syntax is used to supply additional arguments to an
allocation function. If used, overload
resolution is performed on a function call created by assembling an
argument list consisting of the amount of
space requested (the first argument) and the expressions in the new-
placement part of the new-expression (thesecond and succeeding
arguments). The first of these arguments has type std::size_t and the
remaining arguments have the corresponding types of the expressions in
the new-placement.

See 5.4.3/13:
[ Note: unless an allocation function is declared with a non-throwing
exception-specification (15.4), it indicates
failure to allocate storage by throwing a std::bad_alloc exception
(Clause 15, 18.6.2.1); it returns a
non-null pointer otherwise. If the allocation function is declared
with a non-throwing exception-specification,
it returns null to indicate failure to allocate storage and a non-null
pointer otherwise. —end note ] If the
allocation function returns null, initialization shall not be done,
the deallocation function shall not be called,
and the value of the new-expression shall be null.


In our case: p is of type U*.
new( p ) T( b );

The standard says in 18.6.1.3 that there is a function pre-defined by
the implementation:
void* operator new( std::size_t, void* ptr ) noexcept
{
return ptr;
}
//it says it only returns ptr, and does nothing else.

It our case, the allocation function returns the value given to its
second parameter, originally from our variable p in the new
expression. But it must check whether it is null or not, and construct
an instance of T only if non-null.

There's no much way to avoid this pointer comparison, in this case,
that you want the object to be created in the address pointed to by p.
Even if you add your own allocation function to be called instead, the
new expression must compare its return value with null to decide
whether to construct.

Maybe if the optimizer could somehow prove that p is null, it would
ommit the comparision.

itaj
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      02-16-2011
On Feb 16, 5:38 pm, itaj sherman <itajsher...@gmail.com> wrote:
> On Feb 16, 2:03 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
>
>
>
> Maybe if the optimizer could somehow prove that p is null, it would
> ommit the comparision.
>


Ofcourse, I meant prove that it isn't null.

Appart from that, I should say, that as the OP, I too see that it can
cause a perfromance problem. The c++ performance attitude demands that
core operations like placement construction do not perform
unneccessary comparisons - that could be instead the responsibility of
the programmer to assure.

To further explain the problem, I explain how I think it could be
solved with a simple change in the standard:

-- one way --

In 18.6.1.3/3
The section about the pre-defined:
void* operator new( std::size_t, void* ) noexcept;
Add a remark that this function must not be called with a second
parameter null, and that would be undefined behaviour.

That implies that the above expression:
new( p ) T( b );
Has defined behaviour only if p is not null. Making it the
responsibility of the programmer to assure that or compare himself.
This means the compiler can always assume that p != NULL, and ommit
the comparison in this case.

-- another way --

If that first way is objected due to possibly breaking existing code,
it can be tweaked with a dummy parameter for that case:

pre defined enum:

namespace std
{
enum non_null_placement_t { non_null_placement };
}

and pre-defined allocation function:
void* operator new( std::size_t, void* ptr,
std::non_null_placement_t ) noexcept
{
return ptr;
}
with the requirement of ptr != NULL, otherwise undefined-behaviour.

And then:
new( p, std::non_null_placement ) T( b );
Has the required semantics.

itaj
 
Reply With Quote
 
itaj sherman
Guest
Posts: n/a
 
      02-16-2011
On Feb 16, 5:38*pm, itaj sherman <itajsher...@gmail.com> wrote:
> On Feb 16, 2:03 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
>
>
>
> Consider an expression like:
> new( a1, a2, a3 ) T( b1, b2, b3 );
>
> In order for the above to compile, there needs to be a function
> declared of the form:
> void* operator new( std::size_t, U1, U2, U3 ) noexcept; //or without
> noexcept.
> That can match the call expression "operator new( a1, a2, a3 )".


That is "operator new( size, a1, a2, a3 )".
That should usually return a pointer to where the object should be
constructed.

>
> That new expression has to call that allocation function and compare
> the return value to null, in order to decide whether to construct an
> object or not. Generally, there's no way for the new expression to
> know apriori that the return value isn't null.
>


itaj
 
Reply With Quote
 
James Kanze
Guest
Posts: n/a
 
      02-17-2011
On Feb 16, 12:03 pm, "Martin B." <0xCDCDC...@gmx.at> wrote:
> On 16.02.2011 11:00, James Kanze wrote:
> > On Jan 29, 3:31 pm, Marc<marc.gli...@gmail.com> wrote:


> >> I have a memory region (I got it through std::aligned_storage), and I
> >> initialize it using placement new. Looking at the generated code
> >> (gcc-4.6), I notice that:
> >> new(location) Type(arguments);
> >> is implemented as:
> >> if(location!=0) call the constructor


> >> this causes quite a bit of overhead when Type is a trivial type (the
> >> compiler can't always prove that location is not null).


> >> I can't seem to find where the standard says that placement new can be
> >> called with a null argument and should do nothing in that case. Is
> >> this the case?


> > The signature of placement new is:
> > void* operator new( size_t, void* ) throw();
> > The "throw()" means that operator new can return a null pointer,
> > and that the generated code must behave correctly if it does.
> > ...


> Interesting conclusion. Could you explain how you arrive at this
> conclusion from the std?


> I'll quote N3092 (p 44:
> - - -
> 18.6.1.3 Placement forms
> 1 (...)
> void* operator new(std::size_t size, void* ptr) throw();
> 2 Returns: ptr.
> 3 Remarks: Intentionally performs no other action.
> 4 [ Example: This can be useful for constructing an
> object at a known address:
> void* place = operator new(sizeof(Something));
> Something* p = new (place) Something();
> —end example ]
> - - -


§3.7.3.1 (which specifies the requirements for allocator
functions), particularly §3.7.3.1/2: "If [the allocation
function] is successful, it shall return the address of the
start of a block of storage whose length in bytes shall be at
least as large as the requested size" and §3.7.3.1/3: "[...] If
an allocation function declared with an empty
exception-specification (15.4), throw(), fails to allocate
storage, it shall return a null pointer." Together, this
specifies clearly that the only legal return values of an
allocator function (and all "operator new" are allocator
functions) are a pointer to valid memory and a null pointer, and
the latter only if the allocator function has an empty exception
specifier.

Since "void* operator new(size_t size, void* ptr) throw()"
returns ptr directly, it stands to reason that ptr must meet the
requirements for a legal return value of this function. Without
the empty exception specification, that would mean a valid
pointer to the requested size; with the empty exception
specification, a null pointer is also valid.

--
James Kanze
 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
How much overhead for a new templated class? jacob navia C++ 9 05-22-2012 08:09 AM
overriding operator new and accessing placement new Mark P C++ 6 04-27-2005 04:17 AM
New Window size and placement gcc HTML 11 06-10-2004 05:14 PM
placement new , is this acceptable? lallous C++ 10 03-06-2004 03:53 AM
is it placement new? Wenjie C++ 0 06-26-2003 11:16 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57