8
votes

I have the following setup:

  1. A Delphi command line application written in Delphi XE5 and built in Debug 64 bit.
  2. A C dll written in Microsoft Visual Studio 2013 and built in Release 64 bit.
  3. The Delphi command line application calls a function in the C dll.

What's unexpected:

  1. When debugging the Delphi command line application within the Delphi XE5 IDE the C dll function call takes 15 s.
  2. When launching the same Delphi command line application directly (without IDE, without Debugger) then the C dll function call takes 0.16 s.

Delphi command line application source code:

program DelphiCpplibraryCall;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  Windows;

type
  TWork = function(Count : Integer) : Integer; cdecl;
var
  Handle : THandle;
  Work   : TWork;
  Result : Integer;
  Freq   : Int64;
  Start  : Int64;
  Stop   : Int64;
begin
  try
    Handle := LoadLibraryEx('worker.dll', 0, LOAD_WITH_ALTERED_SEARCH_PATH);
    Work := GetProcAddress(Handle, 'work');

    QueryPerformanceFrequency(Freq);
    QueryPerformanceCounter(Start);
    Result := Work(500000);
    QueryPerformanceCounter(Stop);
    Writeln(Format('Result: %d Time: %.6f s', [Result, (Stop-Start) / Freq]));

    FreeLibrary(Handle);

    Readln;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

C dll source code:

#include "worker.h"
#include <unordered_map>

class Item
{
public:
    Item(const int value = 0) : _value(value) {}
    virtual ~Item(void) {}
private:
    int _value;
};

int work(int count)
{
    typedef std::unordered_map<int, Item> Values;
    Values* values = new Values;
    int k = 0;
    for (size_t i = 0; i < count; i++)
    {
        (*values)[i] = Item(i);
        k++;
    }
    delete values;
    return k;
}

Delphi + C dll soure code: DelphiCpplibraryCall.zip

Runtime comparison:

  • First console: When debugging in IDE
  • Second console: When launching without IDE

Runtime comparison

For some reason the the Delphi Debugger seems to slow down the C dll function call a lot which makes debugging nearly impossible.

Has anyone a clue what could case this issue or how to avoid it? Many thanks.

Edit: I can confirm now that the described behavior is not restricted to the Delphi IDE and Debugger at all. This problem also happens if I:

  1. I build the C dll in Microsoft Visual Studio 2013 in Release.
  2. And build and debug the command line executable that calls the C dll in Visual Studio 2013.

This means that the C dll release build function execution time changes depending on whether there is a debugger attached or not.

I can also confirm that it is the deletion of the unordered_map (delete values;) that takes that long as soon as there is a debugger present.

2
Your DLL is actually implemented in C++. My guess is that the C++ runtime detects the presence of a debugger and adds extra self checking of memory access, bounds checking etc. Have you tried a release build of the DLL? Your C++ code is also very inefficient. - David Heffernan
Actually, I see that you are compiling release build of DLL. That probably makes this a Delphi problem. The delphi x64 debugger is terrible. I personally have given up on it because it is so useless. Debug the code in 32 bit. - David Heffernan
@DavidHeffernan As described the C dll is already a release build. The C dll function implementation perfectly shows the runtime difference please ignore the inefficient code. - denim
@DavidHeffernan I should have mentioned that I have already tried the Delphi 32 bit debugger. You are right the problem is not that bad with the 32 bit Delphi debugger but still the runtime is terrible long. - denim
I'll try and repro. See if that sheds any light. Presumably you've cut this down from a real project and isolated unordered_map. And btw, great work asking the question. Good to see such a clean example. - David Heffernan

2 Answers

6
votes

If the delay comes from the delete call then it may be the memory manager (malloc) that uses the Windows heap memory manager. The heap memory manager executes additional extensive checks when memory is released and a debugger is attached.

These additional checks can be disabled by setting the environment variable _NO_DEBUG_HEAP to 1 (starts with an underscore).

You can do this in Delphi for a specific project under Project/Options.../Debugger/Environment Block or you can add _NO_DEBUG_HEAP to the user's environment block under Control Panel/System Properties/Advanced System Settings/Environment Variables/System Variables so it works for all projects and all applications. But then you may need to logout to apply the changes or at least restart the IDE.

Delphi Project Options Environment Block

3
votes

This appears to be an issue with the MSVC implementation of this STL container. Exactly the same behaviour can be seen when you use the Visual Studio debugger. The MSVC runtime is switching behaviour when it detects a debugger.

I've found the following links that relate to this issue:

A large part of the problem appears to be the performance when clearing the map. Simply remove the delete line from your C++ code to see much improved performance.

I cannot find a viable work around to this problem.