[英]VS2012 compiler Strange memory deallocation issues
我有一个VS2012编译器的奇怪问题,似乎没有出现在GCC中。 解除分配过程最终需要几分钟而不是几秒钟。 有人对此有任何意见吗? 步调试显示在调用RtlpCollectFreeBlocks()时显着挂起。 我在调试和发布模式下都有这个问题。 我正在运行Windows 7 32位,但我在64位7上遇到了同样的问题。
#include "stdafx.h"
#include <iostream>
#include <stdint.h>
#include <cstdlib>
#define SIZE 500000
using namespace std;
typedef struct
{
uint32_t* thing1;
}collection;
/*
* VS2012 compiler used.
* Scenarios:
* 1) Don't allocate thing1. Program runs poorly.
* 2) Allocate thing1 but don't delete it. Program runs awesome.
* 3) Allocate thing1 and delete it. Program runs poorly.
*
* Debug or Release mode does not affect outcome. GCC's compiler is fine.
*/
int _tmain(int argc, _TCHAR* argv[])
{
collection ** colArray = new collection*[SIZE];
for(int i=0;i<SIZE;i++)
{
collection * mine = new collection;
mine->thing1 = new uint32_t; // Allocating without freeing runs fine. Either A) don't allocate or B) allocate and delete to make it run slow.
colArray[i] = mine;
}
cout<<"Done with assignment\n";
for(int i=0;i<SIZE;i++)
{
delete(colArray[i]->thing1); // delete makes it run poorly.
delete(colArray[i]);
if(i > 0 && i%100000 == 0)
{
cout<<"100 thousand deleted\n";
}
}
delete [] colArray;
cout << "Done!\n";
int x;
cin>>x;
}
您所看到的性能影响来自Windows调试堆功能,即使在发布版本中,它在启用自身方面也有一点隐秘性。
我冒昧地构建了一个更简单程序的64位调试图像,然后发现了这个:
我特别感兴趣的是msvcr110d.dll!_CrtIsValidHeapPointer
的主体,结果是:
if (!pUserData)
return FALSE;
// Note: all this does is checks for null
if (!_CrtIsValidPointer(pHdr(pUserData), sizeof(_CrtMemBlockHeader), FALSE))
return FALSE;
// but this is e-x-p-e-n-s-i-v-e
return HeapValidate( _crtheap, 0, pHdr(pUserData) );
HeapValidate()
调用是残酷的。
好的,也许我会在调试版本中期待这个。 但肯定不会发布。 事实证明,这会变得更好,但看看调用堆栈:
这很有趣,因为当我先运行它,然后使用IDE(或WinDbg)连接到正在运行的进程而不允许它控制执行启动环境时,此调用栈将停止在ntdll.dll!RtlFreeHeap()
。 换句话说,不会在IDE RtlDebugFreeHeap
外部运行。 但为什么??
我心里想, 不知怎的 ,调试器正在翻转一个开关来启用堆调试。 在做了一些挖掘后,我发现“switch”是调试器本身。 如果正在运行的进程由调试器生成,则Windows使用特殊的调试堆函数( RtlDebugAllocHeap
和RtlDebugFreeHeap
)。 这篇来自MSDN on WinDbg的手册页 ,以及有关Windows下调试的其他有趣内容:
从使用WinDbg调试用户模式进程
调试器创建的进程(也称为生成进程)的行为与调试器未创建的进程略有不同。
调试器创建的进程使用特殊的调试堆,而不是使用标准堆API。 您可以使用_NO_DEBUG_HEAP环境变量或-hd命令行选项强制生成的进程使用标准堆而不是调试堆。
现在我们到了某个地方。 为了测试这个,我简单地放了一个sleep()
,给我一个适当的时间来附加调试器,而不是用它生成进程,然后让它以它的快乐方式运行。 果然,如前所述,它全速前进。
基于该文章的内容,我可以自由更新我的发布模式构建,以在我的项目文件的执行环境设置中定义_NO_DEBUG_HEAP=1
。 我显然仍然对调试版本中的粒度堆活动感兴趣,因此这些配置保持原样。 执行此操作后,我在VS2012(和VS2010)下运行的版本的整体速度大大加快,我邀请您尝试。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.