diff mbox

[v3] Slightly improve operator new

Message ID alpine.DEB.2.02.1403022113150.1515@stedding.saclay.inria.fr
State New
Headers show

Commit Message

Marc Glisse March 2, 2014, 9:24 p.m. UTC
Hello,

inlining operator new (with LTO or otherwise), I noticed that it has a 
complicated implementation, which makes it hard to use this inlined code 
for optimizations. This patch does two things:

1) there are 2 calls to malloc, I am turning them into just one. At -Os, 
it does not change the generated code (RTL optimizers manage to merge the 
calls to malloc). At other levels (-O2, -O3, and especially with -g) it 
gives a smaller object file. And with just one malloc, some optimizations 
become much easier (see my recent calloc patch for instance).

2) malloc is predicted to return null 19 times out of 20 because of the 
loop (that didn't change with the patch), so I am adding __builtin_expect 
to let gcc optimize the fast path.

Further discussion:

a) I didn't add __builtin_expect for the test (sz == 0), it didn't change 
the generated code in my limited test. I was wondering if this test is 
necessary (new doesn't seem to ever call operator new(0)) or could be 
moved to operator new[] (new type[0] does call operator new[](0)), but 
since one can call operator new directly, it has to be protected indeed, 
so let's forget this point ;-)
(too bad malloc is replacable, so we can't use the fact that glibc already 
does the right thing)

b) I have a bit of trouble parsing the standard. Is the nothrow operator 
new supposed to call the regular operator new? In particular, if a user 
replaces only the throwing operator new, should the nothrow operator new 
automatically call that function? That's not what we are currently doing 
(and it would be a perf regression).

"Required behavior: Return a non-null pointer to suitably aligned storage 
(3.7.4), or else return a null pointer. This nothrow version of operator 
new returns a pointer obtained as if acquired from the (possibly replaced) 
ordinary version. This requirement is binding on a replacement version of 
this function.

Default behavior: Calls operator new(size). If the call returns normally, 
returns the result of that call. Otherwise, returns a null pointer."



Passes bootstrap+testsuite on x86_64-linux-gnu. Stage 1?

2014-03-03  Marc Glisse  <marc.glisse@inria.fr>

 	* libsupc++/new_op.cc: Factor the calls to malloc, use __builtin_expect.
 	* libsupc++/new_opnt.cc: Likewise.

Comments

Marc Glisse April 15, 2014, 6:41 p.m. UTC | #1
Ping
http://gcc.gnu.org/ml/gcc-patches/2014-03/msg00051.html


On Sun, 2 Mar 2014, Marc Glisse wrote:

> Hello,
>
> inlining operator new (with LTO or otherwise), I noticed that it has a 
> complicated implementation, which makes it hard to use this inlined code for 
> optimizations. This patch does two things:
>
> 1) there are 2 calls to malloc, I am turning them into just one. At -Os, it 
> does not change the generated code (RTL optimizers manage to merge the calls 
> to malloc). At other levels (-O2, -O3, and especially with -g) it gives a 
> smaller object file. And with just one malloc, some optimizations become much 
> easier (see my recent calloc patch for instance).
>
> 2) malloc is predicted to return null 19 times out of 20 because of the loop 
> (that didn't change with the patch), so I am adding __builtin_expect to let 
> gcc optimize the fast path.
>
> Further discussion:
>
> a) I didn't add __builtin_expect for the test (sz == 0), it didn't change the 
> generated code in my limited test. I was wondering if this test is necessary 
> (new doesn't seem to ever call operator new(0)) or could be moved to operator 
> new[] (new type[0] does call operator new[](0)), but since one can call 
> operator new directly, it has to be protected indeed, so let's forget this 
> point ;-)
> (too bad malloc is replacable, so we can't use the fact that glibc already 
> does the right thing)
>
> b) I have a bit of trouble parsing the standard. Is the nothrow operator new 
> supposed to call the regular operator new? In particular, if a user replaces 
> only the throwing operator new, should the nothrow operator new automatically 
> call that function? That's not what we are currently doing (and it would be a 
> perf regression).
>
> "Required behavior: Return a non-null pointer to suitably aligned storage 
> (3.7.4), or else return a null pointer. This nothrow version of operator new 
> returns a pointer obtained as if acquired from the (possibly replaced) 
> ordinary version. This requirement is binding on a replacement version of 
> this function.
>
> Default behavior: Calls operator new(size). If the call returns normally, 
> returns the result of that call. Otherwise, returns a null pointer."
>
>
>
> Passes bootstrap+testsuite on x86_64-linux-gnu. Stage 1?
>
> 2014-03-03  Marc Glisse  <marc.glisse@inria.fr>
>
> 	* libsupc++/new_op.cc: Factor the calls to malloc, use 
> __builtin_expect.
> 	* libsupc++/new_opnt.cc: Likewise.
>
>
Marc Glisse May 17, 2014, 4:30 p.m. UTC | #2
Ping.

On Tue, 15 Apr 2014, Marc Glisse wrote:

> Ping
> http://gcc.gnu.org/ml/gcc-patches/2014-03/msg00051.html
>
>
> On Sun, 2 Mar 2014, Marc Glisse wrote:
>
>> Hello,
>> 
>> inlining operator new (with LTO or otherwise), I noticed that it has a 
>> complicated implementation, which makes it hard to use this inlined code 
>> for optimizations. This patch does two things:
>> 
>> 1) there are 2 calls to malloc, I am turning them into just one. At -Os, it 
>> does not change the generated code (RTL optimizers manage to merge the 
>> calls to malloc). At other levels (-O2, -O3, and especially with -g) it 
>> gives a smaller object file. And with just one malloc, some optimizations 
>> become much easier (see my recent calloc patch for instance).
>> 
>> 2) malloc is predicted to return null 19 times out of 20 because of the 
>> loop (that didn't change with the patch), so I am adding __builtin_expect 
>> to let gcc optimize the fast path.
>> 
>> Further discussion:
>> 
>> a) I didn't add __builtin_expect for the test (sz == 0), it didn't change 
>> the generated code in my limited test. I was wondering if this test is 
>> necessary (new doesn't seem to ever call operator new(0)) or could be moved 
>> to operator new[] (new type[0] does call operator new[](0)), but since one 
>> can call operator new directly, it has to be protected indeed, so let's 
>> forget this point ;-)
>> (too bad malloc is replacable, so we can't use the fact that glibc already 
>> does the right thing)
>> 
>> b) I have a bit of trouble parsing the standard. Is the nothrow operator 
>> new supposed to call the regular operator new? In particular, if a user 
>> replaces only the throwing operator new, should the nothrow operator new 
>> automatically call that function? That's not what we are currently doing 
>> (and it would be a perf regression).
>> 
>> "Required behavior: Return a non-null pointer to suitably aligned storage 
>> (3.7.4), or else return a null pointer. This nothrow version of operator 
>> new returns a pointer obtained as if acquired from the (possibly replaced) 
>> ordinary version. This requirement is binding on a replacement version of 
>> this function.
>> 
>> Default behavior: Calls operator new(size). If the call returns normally, 
>> returns the result of that call. Otherwise, returns a null pointer."
>> 
>> 
>> 
>> Passes bootstrap+testsuite on x86_64-linux-gnu. Stage 1?
>> 
>> 2014-03-03  Marc Glisse  <marc.glisse@inria.fr>
>>
>> 	* libsupc++/new_op.cc: Factor the calls to malloc, use 
>> __builtin_expect.
>> 	* libsupc++/new_opnt.cc: Likewise.
Jonathan Wakely May 17, 2014, 4:38 p.m. UTC | #3
On 17/05/14 18:30 +0200, Marc Glisse wrote:
>Ping.
>
>On Tue, 15 Apr 2014, Marc Glisse wrote:
>
>>Ping
>>http://gcc.gnu.org/ml/gcc-patches/2014-03/msg00051.html

The patch is OK for trunk - sorry for forgetting about it.
diff mbox

Patch

Index: libsupc++/new_op.cc
===================================================================
--- libsupc++/new_op.cc	(revision 208255)
+++ libsupc++/new_op.cc	(working copy)
@@ -39,22 +39,21 @@  extern "C" void *malloc (std::size_t);
 #endif
 
 _GLIBCXX_WEAK_DEFINITION void *
 operator new (std::size_t sz) _GLIBCXX_THROW (std::bad_alloc)
 {
   void *p;
 
   /* malloc (0) is unpredictable; avoid it.  */
   if (sz == 0)
     sz = 1;
-  p = (void *) malloc (sz);
-  while (p == 0)
+
+  while (__builtin_expect ((p = malloc (sz)) == 0, false))
     {
       new_handler handler = std::get_new_handler ();
       if (! handler)
 	_GLIBCXX_THROW_OR_ABORT(bad_alloc());
       handler ();
-      p = (void *) malloc (sz);
     }
 
   return p;
 }
Index: libsupc++/new_opnt.cc
===================================================================
--- libsupc++/new_opnt.cc	(revision 208255)
+++ libsupc++/new_opnt.cc	(working copy)
@@ -32,30 +32,28 @@  using std::bad_alloc;
 extern "C" void *malloc (std::size_t);
 
 _GLIBCXX_WEAK_DEFINITION void *
 operator new (std::size_t sz, const std::nothrow_t&) _GLIBCXX_USE_NOEXCEPT
 {
   void *p;
 
   /* malloc (0) is unpredictable; avoid it.  */
   if (sz == 0)
     sz = 1;
-  p = (void *) malloc (sz);
-  while (p == 0)
+
+  while (__builtin_expect ((p = malloc (sz)) == 0, false))
     {
       new_handler handler = std::get_new_handler ();
       if (! handler)
 	return 0;
       __try
 	{
 	  handler ();
 	}
       __catch(const bad_alloc&)
 	{
 	  return 0;
 	}
-
-      p = (void *) malloc (sz);
     }
 
   return p;
 }