5
#include <iostream>
#include <vector>
using namespace std;

int main()
{
    vector< vector<int> > dp(50000, vector<int>(4, -1));
    cout << dp.size();
}

仅从命令行运行时,这个小程序需要一秒钟的时间来执行。但在调试器中运行时,需要 8 秒以上。暂停调试器表明它正在销毁所有这些向量。怎么回事?

注意 - Visual Studio 2008 SP1,Core 2 Duo 6700 CPU,2GB RAM。

补充:为了澄清,不,我不会混淆调试和发布版本。这些结果在同一个 .exe 上,中间甚至没有任何重新编译。事实上,在 Debug 和 Release 构建之间切换并没有改变。

4

8 回答 8

18

在调试器中运行会将用于进行更多检查的内存分配库更改为一个。除了内存分配和解除分配之外什么都不做的程序将比“正常”程序遭受更多的痛苦。

编辑 刚刚尝试在VS下运行您的程序,我得到一个看起来像的调用堆栈

ntdll.dll!_RtlpValidateHeapEntry@12()  + 0x117 bytes    
ntdll.dll!_RtlDebugFreeHeap@12()  + 0x97 bytes  
ntdll.dll!_RtlFreeHeapSlowly@12()  + 0x228bf bytes  
ntdll.dll!_RtlFreeHeap@12()  + 0x17646 bytes    
msvcr90d.dll!_free_base(void * pBlock=0x0061f6e8)  Line 109 + 0x13 bytes
msvcr90d.dll!_free_dbg_nolock(void * pUserData=0x0061f708, int nBlockUse=1)
msvcr90d.dll!_free_dbg(void * pUserData=0x0061f708, int nBlockUse=1) 
msvcr90d.dll!operator delete(void * pUserData=0x0061f708)
desc.exe!std::allocator<int>::deallocate(int * _Ptr=0x0061f708, unsigned int __formal=4)
desc.exe!std::vector<int,std::allocator<int> >::_Tidy()  Line 1134  C++

其中显示了 ntdll.dll 中的调试函数和正在使用的 C 运行时。

于 2009-02-10T12:25:35.273 回答
3

运行带有调试器的程序总是比没有运行程序要慢。

这一定是由于 VS 挂钩新/删除调用并在附加时进行更多检查 - 或者运行时库使用 IsDebuggerPresent API 并在这种情况下做不同的事情。

您可以在 Visual Studio 中轻松尝试此操作,使用 Debug->Start Debugging 或 Debug->Start without Debugging 启动程序。没有调试就像从命令行一样,具有完全相同的构建配置和可执行文件。

于 2009-02-10T12:23:02.003 回答
3

当您在调试器中启动程序时,调试堆会自动启用,而不是使用调试器附加到已经运行的程序。

Mario Hewardt 和 Daniel Pravat所著的Advanced Windows Debugging一书提供了一些关于 Windows 堆的不错信息,事实证明,关于堆的章节作为示例章节已在网站上发布

第 281 页有一个关于“附加与在调试器下启动进程”的侧边栏:

在调试器下启动进程时,堆管理器会修改所有请求以创建新堆并更改堆创建标志以启用调试友好的堆(除非 _NO_DEBUG_HEAP 环境变量设置为 1)。相比之下,附加到已经运行的进程,进程中的堆已经使用默认堆创建标志创建,并且不会设置调试友好标志(除非应用程序明确设置)。

(另外:一个半相关的问题,我之前发布了这个答案的一部分。)

于 2009-02-11T03:05:12.040 回答
2

绝对是 HeapFree 减慢了速度,您可以使用下面的程序获得相同的效果。

将 HEAP_NO_SERIALIZE 等参数传递给 HeapFree 也无济于事。

#include "stdafx.h"
#include <iostream>
#include <windows.h>

using namespace std;


int _tmain(int argc, _TCHAR* argv[])
{
HANDLE heap = HeapCreate(0, 0, 0);

void** pointers = new void*[50000];

int i = 0;
for (i = 0; i < 50000; ++i)
{
    pointers[i] = HeapAlloc(heap, 0, 4 * sizeof(int));
}

cout << i;
for (i = 49999; i >= 0; --i)
{
    HeapFree(heap, 0, pointers[i]);
}

cout << "!";

delete [] pointers;

HeapDestroy(heap);
}
于 2009-02-10T13:28:30.567 回答
1

http://www.symantec.com/connect/articles/windows-anti-debug-reference

阅读第 2 节“PEB!NtGlobalFlags”和第 2 节“堆标志”

认为这可以解释它...


编辑:添加解决方案

在 CREATE_PROCESS_DEBUG_EVENT 的处理程序中,添加以下内容

// hack 'Load Configuration Directory' in exe header to point to a new block that specfies GlobalFlags 
IMAGE_DOS_HEADER dos_header;
ReadProcessMemory(cpdi.hProcess,cpdi.lpBaseOfImage,&dos_header,sizeof(IMAGE_DOS_HEADER),NULL);
IMAGE_OPTIONAL_HEADER32 pe_header;
ReadProcessMemory(cpdi.hProcess,(BYTE*)cpdi.lpBaseOfImage+dos_header.e_lfanew+4+sizeof(IMAGE_FILE_HEADER),&pe_header,offsetof(IMAGE_OPTIONAL_HEADER32,DataDirectory),NULL);
IMAGE_LOAD_CONFIG_DIRECTORY32 ilcd;
ZeroMemory(&ilcd,sizeof(ilcd));
ilcd.Size = 64; // not sizeof(ilcd), as 2000/XP didn't have SEHandler
ilcd.GlobalFlagsClear = 0xffffffff; // clear all flags.  this is as we don't want dbg heap
BYTE *p = (BYTE *)VirtualAllocEx(cpdi.hProcess,NULL,ilcd.Size,MEM_COMMIT|MEM_RESERVE,PAGE_READWRITE);
WriteProcessMemory(cpdi.hProcess,p,&ilcd,ilcd.Size,NULL);
BYTE *dde = (BYTE*)cpdi.lpBaseOfImage+dos_header.e_lfanew+4+sizeof(IMAGE_FILE_HEADER)+offsetof(IMAGE_OPTIONAL_HEADER32,DataDirectory)+sizeof(IMAGE_DATA_DIRECTORY)*IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG;
IMAGE_DATA_DIRECTORY temp;
temp.VirtualAddress = p-cpdi.lpBaseOfImage;
temp.Size = ilcd.Size;
DWORD oldprotect;
VirtualProtectEx(cpdi.hProcess,dde,sizeof(temp),PAGE_READWRITE,&oldprotect);
WriteProcessMemory(cpdi.hProcess,dde,&temp,sizeof(temp),NULL);
VirtualProtectEx(cpdi.hProcess,dde,sizeof(temp),oldprotect,&oldprotect);
于 2010-03-27T03:08:18.970 回答
0

是的,确实是WTF。

您知道您的编译器将通过内联它们来优化许多这些函数调用,然后进一步优化那里的代码以排除任何实际上没有做任何事情的东西,这对于 int 的向量意味着:几乎不是很多.

在调试模式下,内联没有打开,因为那会使调试变得很糟糕。

这是一个很好的例子,说明了 C++ 代码的速度有多快。

于 2009-02-10T12:23:33.733 回答
0

8秒??我在调试模式下尝试了同样的方法。我猜不超过半秒。你确定是破坏者?

供参考。Visual Studio 2008 SP1,Core 2 Duo 6700 CPU,2GB RAM。

于 2009-02-10T14:34:42.593 回答
-1

对我来说毫无意义 - 在正常配置中将调试器附加到随机二进制文件应该主要只是捕获断点中断(asm int 3 等)。

于 2009-02-10T13:26:00.980 回答