Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird behaviour of C++ destructors

#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector< vector<int> > dp(50000, vector<int>(4, -1));
    cout << dp.size();
}

This tiny program takes a split second to execute when simply run from the command line. But when run in a debugger, it takes over 8 seconds. Pausing the debugger reveals that it is in the middle of destroying all those vectors. WTF?

Note - Visual Studio 2008 SP1, Core 2 Duo 6700 CPU with 2GB of RAM.

Added: To clarify, no, I'm not confusing Debug and Release builds. These results are on one and the same .exe, without even any recompiling inbetween. In fact, switching between Debug and Release builds changes nothing.

like image 332
Vilx- Avatar asked Feb 10 '09 12:02

Vilx-


3 Answers

Running in the debugger changes the memory allocation library used to one that does a lot more checking. A program that does nothing but memory allocation and de-allocation is going to suffer much more than a "normal" program.

Edit Having just tried running your program under VS I get a call stack that looks like

ntdll.dll!_RtlpValidateHeapEntry@12()  + 0x117 bytes    
ntdll.dll!_RtlDebugFreeHeap@12()  + 0x97 bytes  
ntdll.dll!_RtlFreeHeapSlowly@12()  + 0x228bf bytes  
ntdll.dll!_RtlFreeHeap@12()  + 0x17646 bytes    
msvcr90d.dll!_free_base(void * pBlock=0x0061f6e8)  Line 109 + 0x13 bytes
msvcr90d.dll!_free_dbg_nolock(void * pUserData=0x0061f708, int nBlockUse=1)
msvcr90d.dll!_free_dbg(void * pUserData=0x0061f708, int nBlockUse=1) 
msvcr90d.dll!operator delete(void * pUserData=0x0061f708)
desc.exe!std::allocator<int>::deallocate(int * _Ptr=0x0061f708, unsigned int __formal=4)
desc.exe!std::vector<int,std::allocator<int> >::_Tidy()  Line 1134  C++

Which shows the debug functions in ntdll.dll and the C runtime being used.

like image 170
Ian G Avatar answered Oct 20 '22 17:10

Ian G


Running a program with the debugger attached is always slower than without.

This must be caused by VS hooking into the new/delete calls and doing more checking when attached - or the runtime library uses IsDebuggerPresent API and does things different in that case.

You can easily try this from inside Visual Studio, start the program with Debug->Start Debugging or Debug->Start Without Debugging. Without debugging is like from command line, with exactly the same build configuration and executable.

like image 39
Timbo Avatar answered Oct 20 '22 19:10

Timbo


The debug heap automatically gets enabled when you start your program in the debugger, as opposed to attaching to an already-running program with the debugger.

The book Advanced Windows Debugging by Mario Hewardt and Daniel Pravat has some decent information about the Windows heap, and it turns out that the chapter on heaps is up on the web site as a sample chapter.

Page 281 has a sidebar about "Attaching Versus Starting the Process Under the Debugger":

When starting the process under the debugger, the heap manager modifies all requests to create new heaps and change the heap creation flags to enable debug-friendly heaps (unless the _NO_DEBUG_HEAP environment variable is set to 1). In comparison, attaching to an already-running process, the heaps in the process have already been created using default heap creation flags and will not have the debug-friendly flags set (unless explicitly set by the application).

(Also: a semi-related question, where I posted part of this answer before.)

like image 4
bk1e Avatar answered Oct 20 '22 17:10

bk1e