Delphi:使用调试器调用 C dll 函数需要 15 秒,没有调试器需要 0.16 秒。为什么?
Delphi: Calling a C dll function with Debugger takes 15 s without debugger 0.16 s. Why?
我有以下设置:
- 用 Delphi XE5 编写并内置 Debug 64 位的 Delphi 命令行应用程序。
- 用 Microsoft Visual Studio 2013 编写并内置于 64 位版本的 C dll。
- Delphi 命令行应用程序调用 C dll 中的函数。
意外情况:
- 在 Delphi XE5 IDE 中调试 Delphi 命令行应用程序时,C dll 函数调用需要 15 秒。
- 当直接启动相同的 Delphi 命令行应用程序时(没有 IDE,没有调试器),C dll 函数调用需要 0.16 秒。
Delphi命令行应用程序源代码:
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源代码:
#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源代码:DelphiCpplibraryCall.zip
运行时比较:
- 第一个控制台:在 IDE
中调试时
- 第二个控制台:在没有 IDE
的情况下启动时
出于某种原因,Delphi 调试器似乎大大减慢了 C dll 函数调用的速度,这使得调试几乎不可能。
有没有人知道什么可能导致这个问题或如何避免它?非常感谢。
编辑: 我现在可以确认所描述的行为根本不限于 Delphi IDE 和调试器。如果我:
,也会发生此问题
- 我在 Microsoft Visual Studio 2013 Release 中构建 C dll。
- 并在 Visual Studio 2013 年构建和调试调用 C dll 的命令行可执行文件。
这意味着 C dll 发布构建函数的执行时间会根据是否附加调试器而变化。
我还可以确认,只要存在调试器,删除 unordered_map (delete values;
) 就需要这么长时间。
这似乎是此 STL 容器的 MSVC 实现的问题。使用 Visual Studio 调试器时可以看到完全相同的行为。 MSVC 运行时在检测到调试器时正在切换行为。
我找到了与此问题相关的以下链接:
- 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/
很大一部分问题似乎是清除地图时的性能问题。只需从您的 C++ 代码中删除 delete
行即可看到显着提高的性能。
我找不到解决此问题的可行方法。
如果延迟来自 delete
调用,则可能是使用 Windows 堆内存管理器的内存管理器 (malloc)。当释放内存并附加调试器时,堆内存管理器会执行额外的广泛检查。
可以通过将环境变量 _NO_DEBUG_HEAP
设置为 1
(以下划线开头)来禁用这些附加检查。
您可以在 Delphi 中针对 Project/Options.../Debugger/Environment Block
下的特定项目执行此操作,或者您可以将 _NO_DEBUG_HEAP
添加到 Control Panel/System Properties/Advanced System Settings/Environment Variables/System Variables
下的用户环境块中,以便它适用于所有人项目和所有应用程序。但随后您可能需要注销以应用更改或至少重新启动 IDE.
我有以下设置:
- 用 Delphi XE5 编写并内置 Debug 64 位的 Delphi 命令行应用程序。
- 用 Microsoft Visual Studio 2013 编写并内置于 64 位版本的 C dll。
- Delphi 命令行应用程序调用 C dll 中的函数。
意外情况:
- 在 Delphi XE5 IDE 中调试 Delphi 命令行应用程序时,C dll 函数调用需要 15 秒。
- 当直接启动相同的 Delphi 命令行应用程序时(没有 IDE,没有调试器),C dll 函数调用需要 0.16 秒。
Delphi命令行应用程序源代码:
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源代码:
#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源代码:DelphiCpplibraryCall.zip
运行时比较:
- 第一个控制台:在 IDE 中调试时
- 第二个控制台:在没有 IDE 的情况下启动时
出于某种原因,Delphi 调试器似乎大大减慢了 C dll 函数调用的速度,这使得调试几乎不可能。
有没有人知道什么可能导致这个问题或如何避免它?非常感谢。
编辑: 我现在可以确认所描述的行为根本不限于 Delphi IDE 和调试器。如果我:
,也会发生此问题- 我在 Microsoft Visual Studio 2013 Release 中构建 C dll。
- 并在 Visual Studio 2013 年构建和调试调用 C dll 的命令行可执行文件。
这意味着 C dll 发布构建函数的执行时间会根据是否附加调试器而变化。
我还可以确认,只要存在调试器,删除 unordered_map (delete values;
) 就需要这么长时间。
这似乎是此 STL 容器的 MSVC 实现的问题。使用 Visual Studio 调试器时可以看到完全相同的行为。 MSVC 运行时在检测到调试器时正在切换行为。
我找到了与此问题相关的以下链接:
- 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/
很大一部分问题似乎是清除地图时的性能问题。只需从您的 C++ 代码中删除 delete
行即可看到显着提高的性能。
我找不到解决此问题的可行方法。
如果延迟来自 delete
调用,则可能是使用 Windows 堆内存管理器的内存管理器 (malloc)。当释放内存并附加调试器时,堆内存管理器会执行额外的广泛检查。
可以通过将环境变量 _NO_DEBUG_HEAP
设置为 1
(以下划线开头)来禁用这些附加检查。
您可以在 Delphi 中针对 Project/Options.../Debugger/Environment Block
下的特定项目执行此操作,或者您可以将 _NO_DEBUG_HEAP
添加到 Control Panel/System Properties/Advanced System Settings/Environment Variables/System Variables
下的用户环境块中,以便它适用于所有人项目和所有应用程序。但随后您可能需要注销以应用更改或至少重新启动 IDE.