Delphi:使用调试器调用 C dll 函数需要 15 秒,而没有调试器需要 0.16 秒。为啥?
Posted
技术标签:
【中文标题】Delphi:使用调试器调用 C dll 函数需要 15 秒,而没有调试器需要 0.16 秒。为啥?【英文标题】:Delphi: Calling a C dll function with Debugger takes 15 s without debugger 0.16 s. Why?Delphi:使用调试器调用 C dll 函数需要 15 秒,而没有调试器需要 0.16 秒。为什么? 【发布时间】:2015-08-27 13:23:15 【问题描述】:我有以下设置:
-
用 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 发布版中构建了 C dll。
并在 Visual Studio 2013 中构建和调试调用 C dll 的命令行可执行文件。
这意味着C dll发布构建函数执行时间会根据是否附加调试器而变化。
我还可以确认,只要存在调试器,删除 unordered_map (delete values;
) 就需要这么长时间。
【问题讨论】:
您的 DLL 实际上是用 C++ 实现的。我的猜测是 C++ 运行时检测到调试器的存在并添加了额外的内存访问自我检查、边界检查等。您是否尝试过 DLL 的发布版本?你的 C++ 代码效率也很低。 实际上,我看到您正在编译 DLL 的发布版本。这可能使这成为德尔福问题。 delphi x64 调试器很糟糕。我个人已经放弃了它,因为它太没用了。调试 32 位代码。 @DavidHeffernan 如上所述,C dll 已经是发布版本。 C dll函数实现完美展现运行时差异请忽略低效代码。 @DavidHeffernan 我应该提到我已经尝试过 Delphi 32 位调试器。你是对的,32 位 Delphi 调试器的问题并没有那么糟糕,但运行时仍然很长。 我会尝试复制。看看这是否能说明问题。大概你已经从一个真实的项目和孤立的 unordered_map 中删除了它。顺便说一句,提出这个问题的工作做得很好。很高兴看到这样一个干净的例子。 【参考方案1】:如果延迟来自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 IDE 中调试命令行应用程序时,我的 C dll 的发布版本也可以快速运行。但是我在 Windows 环境变量设置中设置了变量。此外,我还找到了准确描述问题的The Windows Heap Is Slow When Launched from the Debugger。【参考方案2】:这似乎是此 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
行即可看到性能大大提高。
我找不到解决此问题的可行方法。
【讨论】:
虽然这对开发人员来说是一个可怕的麻烦,但幸运的是这在发布版本中不会发生。以上是关于Delphi:使用调试器调用 C dll 函数需要 15 秒,而没有调试器需要 0.16 秒。为啥?的主要内容,如果未能解决你的问题,请参考以下文章