Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi: Calling a C dll function with Debugger takes 15 s without debugger 0.16 s. Why?

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.

like image 905
denim Avatar asked Aug 27 '15 13:08

denim


People also ask

How do I debug DLL files?

Debug from the DLL project Set breakpoints in the DLL project. Right-click the DLL project and choose Set as Startup Project. Make sure the Solutions Configuration field is set to Debug. Press F5, click the green Start arrow, or select Debug > Start Debugging.

How do I debug in Delphi?

Debugging delphi source files You need to go to "project->options->compiler" on this tab you need to check the "use debug DCUs". After that you need to build your project again and you can run your application. From now on breakpoints also stop in Delphi source files.

How do I enable debugging in Visual Studio?

In the Visual Studio toolbar, make sure the configuration is set to Debug. To start debugging, select the profile name in the toolbar, such as <project profile name>, IIS Express, or <IIS profile name> in the toolbar, select Start Debugging from the Debug menu, or press F5.


2 Answers

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

like image 172
Andreas Hausladen Avatar answered Sep 23 '22 09:09

Andreas Hausladen


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:

  • Why hash_map and unordered_map on my machine are extremely slow?
  • http://www.drdobbs.com/cpp/c11-hash-containers-and-debug-mode/232200410
  • http://marknelson.us/2011/11/28/vc-10-hash-table-performance-problems/
  • https://connect.microsoft.com/VisualStudio/feedback/details/757277/c-hash-map-destructor-is-much-slower-in-vc10-than-vc9
  • http://w3facility.org/question/very-slow-unordered_map-clearing/

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.

like image 45
David Heffernan Avatar answered Sep 25 '22 09:09

David Heffernan