Why is my app so much slower under the debugger?

Even when I switch to "Release builds"?  We recently got a connect bug on this so I thought I'd share my thoughts.

In the debugger, we always try to minimize the impact we introduce on the debuggee, but there is always some work that we do in a synchronous way.  We alway deal with breakpoints, even when they are in "tracepoint mode",  we always do work when receiving a module load or thread create event.  As I say, we try to minimize the impact of this work, and are constantly looking to improve the performance (and therefor usually the perturbance) on the target application.  However the biggest contributor I see to app performance degradation is the fact that when launching under the debugger, the operating system uses the debug heap. 

The VS debugger uses the standard Windows CreateProcess call to create the debuggee.  See docs for that here: https://msdn.microsoft.com/en-us/library/ms682425(VS.85).aspx. We pass in the create flag DEBUG_PROCESS as documented here: https://msdn.microsoft.com/en-us/library/ms684863(VS.85).aspx

When we do that, Windows enables three heap debugging features:

  1. Enabling a tail check. This put a marker on the end of each allocation and when it is freed, the heap manager checks the marker is intact.
  2. Enabling free check.  If the process writes to the block after free, the heap manager detects this.
  3. Checking paramaters.  The heap manager checks for valid parameters in each call to any of the heap allocation/free functions.

1. and 2. have a sizable performance impact when freeing memory.  For more information on this subject, read Mark Russinovich and David Solomon's book "Windows Internals" Chapter 7.

There are two workarounds for this issue. 

  1. Use CTRL+F5 and attach to your application instead.  I realize this is not always ideal.
  2. Add the setting _NO_DEBUG_HEAP=1 to the environment settings in the debugger settings for your launch project.  This only works in C++ projects today.  You could force inheritance here by adding that to Visual Studio's environment, which your target application inherits from.

Remember however that you are giving up a lot of validation that the OS does on your behalf.  Catching memory corruption issues early is very important, so don't take this step blindly.

 Happy hunting

John