C++: performance, free(null)/delete null and `std::move`

222 Views Asked by At

I know that calling free with a null pointer is a NO-OP, free already has a check for that, but it's also true that free is a libc library function that might have some fixed overhead respect to doing nothing.

My question is, is it worth to consider replacing this:

delete ptr;

by this?

if (ptr)
   delete ptr;

First, do delete do a null check before forwarding the call to free? or do delete usually forwards the call in an absolutely transparent way?

If that's the case, we can talk in terms of free from now on.

So, is the transformation above worth it in order to avoid a potentially expensive free execution that does nothing? In C++ it might be a thing to consider because of std::moveing objects around might leave a graveyard of empty objects behind while reaching to its final destination; objects whose destructors will call free with null pointers.

Checking the glibc source code, I found this:

void
free (void *ptr)
{
  struct header *real;

  /* Determine real implementation if not already happened.  */
  if (__glibc_unlikely (initialized <= 0))
    {
      if (initialized == -1)
        return;

      me ();
    }

  /* If this is not the correct program just use the normal function.  */
  if (not_me)
    {
      (*freep) (ptr);
      return;
    }

  /* `free (NULL)' has no effect.  */
  if (ptr == NULL)
    {
      catomic_increment (&calls[idx_free]);
      return;
    }

  // rest of the implementation

so calling it looks like not "free" at all; there's stuff below and even an atomic increment before the null check, but it's also true that this implementation might be a fallback implementation or something; that's not the first time I'm reading a library function whose actual implementation is done somewhere else, in assembly or something, or there's specialized versions for different archs or whatever.

2

There are 2 best solutions below

0
user17732522 On

First, do delete do a null check before forwarding the call to free?

delete never directly calls free. delete calls some deallocation function, i.e. some function with name operator delete. The global replaceable operator delete overloads have a library implementation that in some suitable way deallocate memory obtained by the corresponding operator new. There is no guarantee that it will use free for this or how it will use free.

The global ::operator delete(void*) is replaceable in any translation unit. Therefore the compiler is not able to see through it or inline it.

Generally a delete expression must always implicitly do a null pointer check anyway, because if a null pointer is passed, then no destructor may be called. If the destructor is trivial or the compiler can inline it to see that it won't do anything, then of course this implicit check might be optimized away.

Whether or not the deallocation function will be called if a null pointer is provided is up to the compiler. So it may or may not be that the compiler performs an implicit null pointer check before calling the deallocation function regardless of your explicit one.

A quick test on godbolt suggests to me that at least both GCC and Clang always emit an implicit null pointer check before calling the deallocation function anyway. So your added explicit check can't positively affect performance.

0
Martin York On

My question is, is it worth to consider replacing this:

delete ptr;
// by this?
if (ptr)
   delete ptr;

Emphatically: NO.

1: This is a micro optimization.

Sure; you may gain something on your specific system (you may be able to prove that). But that does not mean your optimization is going to hold for anything but the particular system you are currently on.

Not only is the optimization bound to your current system, but the current version of the compiler and the current version of the standard library you are using.

So if you do this, you have to then enclose your if (ptr) between some macros that validate that you are building for a very specific implementation and over time you will then expand that for every system you can prove it for.

2: Every engineer that comes after you and reads the code is going to go: WHAT IS THIS. Why was the last person doing this micro optimization. So you then have to include detailed comments explaining WHY you have done it and provide the appropriate data the proves it is worth while.

Unless you see some significant improvement, the next person is simply going to delete it because the maintenance cost for subsequent engineers is simply not worth it (as they have to parse and understand the comment, I don't believe that and test it for themselves).


With no data to back me up. I would assume that the cost of doing the testing and the extra cognitive cycles used by subsequent engineers maintaining the code will far exceed any improvements you receive for the full lifetime of the code.

EVEN IF I FOUND A SIGNIFICANT IMPROVEMENT, the answer is still no. But in this situation, my course of action would take the data to the people that write the standard library and/or compiler and get the appropriate changes done at the compiler level.