home bbs files messages ]

Forums before death by AOL, social media and spammers... "We can't have nice things"

   comp.lang.c++.moderated      Moderated discussion of C++ superhackery      33,346 messages   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]

   Message 31,742 of 33,346   
   Martin B. to Le Chaud Lapin   
   Re: Cost of deleting a null pointer (1/2   
   15 Dec 11 15:43:07   
   
   8eb3457c   
   From: 0xCDCDCDCD@gmx.at   
      
   On 15.12.2011 00:24, Le Chaud Lapin wrote:   
   > On Nov 30, 10:29 pm, "A. McKenney"  wrote:   
   >> Recently, someone was doing some profiling of our code, and found that   
   >> code essentially like the following was taking far more time than   
   >> expected:   
   >> (...)   
   >>     for ( int i = 0; i<  18; ++i )   
   >>       {   
   >>          delete tlist[i];   
   >>          tlist[i] = 0;   
   >>       }   
   >>   }   
   >>   
   >> In most cases, all the elements of tlist were null.   
   >>   
   >> He found that if he replaced   
   >>   
   >>      delete tlist[i];   
   >>   
   >> with   
   >>   
   >>     if ( tlist[i] ) delete tlist[i];   
   >>   
   >> it sped the function up by about a factor of 30.  His assumption was   
   >> that all the destructor and freeing logic was being gone through   
   >> even when the pointer was null.   
   >> (...)   
   >   
   > I had assumed the same thing when I experienced something similar a   
   > few years ago. See below.   
   >   
   >> Is this just a Quality of Implementation issue, or is it reasonable to   
   >> expect that deleting a null pointer should be expensive?   
   >   
   > (...)  so I went snooping around for   
   > opportunities to optimize. To my surprise, the compiler was not   
   > checking to see if the argument of delete() was 0 before invoking the   
   > machinery that effects delete().   
      
   Was this type perchance living in a DLL? (I haven't have time to try that out   
   like below.)   
      
   > So I added a test, as you did above,   
   > and the difference was dramatic. I do not remember if it was 30x, nor   
   > if I was using Debug or Release code (on Visual Studio 2008) were the   
   > Debug configuration might add some extraneous fat just before   
   > invocation, but I do remember that the difference was so great that I   
   > made a note to self: "Explicitly test for 0 on delete from now on." I   
   > also recall, after a thorough examination of the dis-assembly and some   
   > reflection, arriving at the conclusion that the compiler writer was   
   > very-well cognizant of what s/he had done and deliberately designed it   
   > that way.   
      
   Since the op failed to mention his compiler, but you mention VS2008, I can add   
   some info wrt. to VS2005 (unfortunately, I do not have a newer version   
   available atm.)   
      
   Here's the disassembly from VS2005 for deleteing some pointers:   
   (Release mode, optimization /Ox - but full-prog-opt disabled)   
   + + + + +   
   	cout << "Deleting pointers ...\n";   
   0040110E  mov         edx,dword ptr [__imp_std::cout (403044h)]   
   00401114  push        offset ___xi_z+30h (40314Ch)   
   00401119  push        edx   
   0040111A  call        std::operator<< > (401310h)   
   0040111F  add         esp,8   
   	delete p1;   
   00401122  test        ebp,ebp   
   00401124  je          main+126h (401136h)   
   00401126  mov         ecx,ebp   
   00401128  call        ValueType::~ValueType (401540h)   
   0040112D  push        ebp   
   0040112E  call        operator delete (401732h)   
   00401133  add         esp,4   
   	delete p2;   
   00401136  test        ebx,ebx   
   00401138  je          main+13Ah (40114Ah)   
   0040113A  mov         ecx,ebx   
   0040113C  call        ValueType::~ValueType (401540h)   
   00401141  push        ebx   
   00401142  call        operator delete (401732h)   
   00401147  add         esp,4   
   	delete pb;   
   0040114A  test        esi,esi   
   0040114C  je          main+148h (401158h)   
   0040114E  mov         eax,dword ptr [esi]   
   00401150  mov         edx,dword ptr [eax]   
   00401152  push        1   
   00401154  mov         ecx,esi   
   00401156  call        edx   
   	delete pd;   
   00401158  test        edi,edi   
   0040115A  je          main+15Ch (40116Ch)   
   0040115C  mov         eax,dword ptr [edi]   
   0040115E  mov         ecx,dword ptr [eax+4]   
   00401161  mov         edx,dword ptr [ecx+edi]   
   00401164  mov         eax,dword ptr [edx]   
   00401166  add         ecx,edi   
   00401168  push        1   
   0040116A  call        eax   
   + + + +   
      
   As can be seen at 401122, 401136, 40114A, 401158 the compiler does in fact   
   check for NULL. (Adding in an `if(p)` will be optimized out by my compiler if   
   I read the other asm correctly.)   
      
   The debug version is rather interesting:   
   + + + +   
   	delete p1;   
   004117B3  mov         eax,dword ptr [ebp-14h]   
   004117B6  mov         dword ptr [ebp-14Ch],eax   
   004117BC  mov         ecx,dword ptr [ebp-14Ch]   
   004117C2  mov         dword ptr [ebp-158h],ecx   
   004117C8  cmp         dword ptr [ebp-158h],0   
   004117CF  je          main+276h (4117E6h)   
   004117D1  push        1   
   004117D3  mov         ecx,dword ptr [ebp-158h]   
   004117D9  call        ValueType::`scalar deleting destructor' (41106Eh)   
   004117DE  mov         dword ptr [ebp-1CCh],eax   
   004117E4  jmp         main+280h (4117F0h)   
   004117E6  mov         dword ptr [ebp-1CCh],0   
   	if(p2) {   
   004117F0  cmp         dword ptr [ebp-20h],0   
   004117F4  je          main+2C3h (411833h)   
   		delete p2;   
   004117F6  mov         eax,dword ptr [ebp-20h]   
   004117F9  mov         dword ptr [ebp-134h],eax   
   004117FF  mov         ecx,dword ptr [ebp-134h]   
   00411805  mov         dword ptr [ebp-140h],ecx   
   0041180B  cmp         dword ptr [ebp-140h],0   
   00411812  je          main+2B9h (411829h)   
   00411814  push        1   
   00411816  mov         ecx,dword ptr [ebp-140h]   
   0041181C  call        ValueType::`scalar deleting destructor' (41106Eh)   
   00411821  mov         dword ptr [ebp-1CCh],eax   
   00411827  jmp         main+2C3h (411833h)   
   00411829  mov         dword ptr [ebp-1CCh],0   
   	}   
   + + + +   
      
   As you can see, the "prelude" takes 4 mov commands without the if.   
   If adding in the if in the debug version, the cmp is done immediately (and   
   another cmp again for delete, since no-optimizations.)   
      
   Obviously, "tweaking" the debug version is not something one normally does.   
   (But you mentioned that you cannot remember if it were relese or debug in your   
   VS2008 case.)   
      
      
   To conclude:   
   VS2005 adds an explicit "if" check before calling the dtor (either normal or   
   virtual) and the operator delete.   
      
   Is there any reason to expect any compiler to do differently?   
      
   cheers,   
   Martin   
      
   p.s.:   
   The test code:   
      
   //types.h   
   class ValueType {   
   public:   
   	ValueType();   
   	~ValueType();   
   };   
      
   class Base {   
   public:   
   	virtual ~Base();   
   };   
      
   class Derived : virtual public Base {   
   public:   
   	Derived();   
   	virtual ~Derived();   
   };   
      
   //types.cpp   
   #include "stdafx.h"   
   #include "types.h"   
      
   using std::cout;   
      
   ValueType::ValueType() {   
   	cout << __FUNCTION__ << "\n";   
   }   
      
   ValueType::~ValueType() {   
   	cout << __FUNCTION__ << "\n";   
   }   
      
   Base::~Base() {   
   	cout << __FUNCTION__ << "\n";   
   }   
      
      
   Derived::Derived() {   
   	cout << __FUNCTION__ << "\n";   
   }   
      
   Derived::~Derived() {   
   	cout << __FUNCTION__ << "\n";   
   }   
      
   //main.cpp   
   #include "stdafx.h"   
   #include "types.h"   
      
      
   int main()   
   {   
   	using namespace std;   
      
   	ValueType *p1, *p2;   
   	Base *pb;   
   	Derived *pd;   
   	if(time(0) > 0) {   
   		p1 = new ValueType();   
   		p2 = NULL;   
   		pb = new Derived();   
   		pd = NULL;   
   	} else {   
   		p1 = NULL;   
   		p2 = new ValueType();   
   		pb = NULL;   
      
   [continued in next message]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   

[   << oldest   |   < older   |   list   |   newer >   |   newest >>   ]


(c) 1994,  bbs@darkrealms.ca