Memory management Windows Vista和Windows 7上使用heap_NO_SERIALIZE的堆内存函数速度降低约100倍的原因

Memory management Windows Vista和Windows 7上使用heap_NO_SERIALIZE的堆内存函数速度降低约100倍的原因,memory-management,winapi,windows-7,windows-vista,heap,Memory Management,Winapi,Windows 7,Windows Vista,Heap,我正试图追踪WindowsVista和Windows7中堆内存功能的巨大减速(我没有在任何服务器版本上进行测试)。这种情况根本不会发生在Windows XP上,只会发生在Microsoft较新的操作系统上 我最初在Windows上编写PHP时遇到了这个问题。脚本本身似乎以预期的速度运行,但是在脚本执行之后,我在内部PHP关闭函数中遇到了1-2秒的延迟。启动调试后,我发现这与PHP内存管理器使用的HeapAlloc/HeapFree/heapreloc有关 我追溯到在堆函数上使用标志HEAP\u

我正试图追踪WindowsVista和Windows7中堆内存功能的巨大减速(我没有在任何服务器版本上进行测试)。这种情况根本不会发生在Windows XP上,只会发生在Microsoft较新的操作系统上

我最初在Windows上编写PHP时遇到了这个问题。脚本本身似乎以预期的速度运行,但是在脚本执行之后,我在内部PHP关闭函数中遇到了1-2秒的延迟。启动调试后,我发现这与PHP内存管理器使用的
HeapAlloc
/
HeapFree
/
heapreloc
有关

我追溯到在堆函数上使用标志
HEAP\u NO\u SERIALIZE

#ifdef ZEND_WIN32
#define ZEND_DO_MALLOC(size) (AG(memory_heap) ? HeapAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, size) : malloc(size))
#define ZEND_DO_FREE(ptr) (AG(memory_heap) ? HeapFree(AG(memory_heap), HEAP_NO_SERIALIZE, ptr) : free(ptr))
#define ZEND_DO_REALLOC(ptr, size) (AG(memory_heap) ? HeapReAlloc(AG(memory_heap), HEAP_NO_SERIALIZE, ptr, size) : realloc(ptr, size))
#else
#define ZEND_DO_MALLOC(size) malloc(size)
#define ZEND_DO_FREE(ptr) free(ptr)
#define ZEND_DO_REALLOC(ptr, size) realloc(ptr, size)
#endif
和(实际设置函数
start\u memory\u manager
中的
HeapAlloc
/
HeapFree
/
heapreloc
)的默认值):

#ifdef ZEND_WIN32
    AG(memory_heap) = HeapCreate(HEAP_NO_SERIALIZE, 256*1024, 0);
#endif
我删除了
HEAP\u NO\u SERIALIZE
参数(替换为0),它修复了这个问题。脚本现在可以在CLI和SAPI Apache 2版本中快速清理。这是针对PHP4.4.9的,但是PHP5和PHP6源代码(在开发中)在调用中包含相同的标志

我不确定我所做的是否危险。这都是PHP内存管理器的一部分,因此我必须进行一些挖掘和研究,但这带来了一个问题:

为什么在Windows Vista和Windows 7上使用
heap\u NO\u SERIALIZE
时堆内存功能会这么慢?

在研究这个问题时,我想出了一个很好的答案。请阅读博客文章,其中海报解释了这个问题,并提供了一个测试用例(源代码和二进制文件都可用)来突出这个问题

我在Windows7 x64四核8GB机器上的测试结果为43836。哎哟没有
HEAP\u NO_SERIALIZE
标志的相同结果是655,在我的例子中大约快了70倍

P>最后,在Visual C++ 6中使用了<代码> MalC/< >代码>免费<代码>或<代码>新< /代码> /<代码>删除>代码>,这些程序似乎都会对这些新平台产生影响。Visual C++ 2008编译器默认不为这些函数/操作符设置此标志,因此它们不会受到影响,但这仍然会影响很多程序!p> 我鼓励您下载概念验证并尝试一下。这个问题解释了为什么我在Windows上的普通PHP安装是爬行的,也解释了为什么Windows Vista和Windows 7有时看起来比较慢


更新2010-01-26:我收到了微软的回复,称(LFH)实际上是持有任何可观数量分配的堆的默认策略。在WindowsVista中,他们重新组织了大量代码,以删除额外的数据结构和代码路径,这些结构和路径不再是处理堆API调用的常见情况的一部分。使用
HEAP\u NO\u SERIALIZE
标志,并且在某些调试情况下,它们不允许使用LFH,因此我们在通过HEAP manager的缓慢且优化程度较低的路径上陷入困境。所以强烈建议不要使用
HEAP\u NO\u SERIALIZE
,因为您将错过LFH的所有工作以及Windows HEAP API中的任何未来工作。

我注意到的第一个区别是Windows Vista始终使用低碎片堆(LFH)。windowsxp似乎没有<因此,Windows Vista中的code>RtlFreeHeap缩短了很多--所有工作都委托给
RtlpLowFragHeapFree
。以及它在各种操作系统中的存在。注意顶部的红色警告

(备注部分): Windows XP、Windows Server 2003和 带修补程序KB 816542的Windows 2000:

旁白列表是一种快速记忆 包含 只有固定大小的块。撇开 默认情况下,为堆启用列表 支持他们以开始 Windows Vista、旁白列表 未使用,LFH由启用 默认值

另一条重要信息:LFH和
NO_SERIALIZE
是互斥的(两者不能同时激活)。结合

从 Windows Vista、旁白列表 不用

这意味着在Windows Vista中设置
NO_SERIALIZE
将禁用LFH,但根据上面的引文,LFH不会(也不能)退回到标准的旁白列表(作为快速替换)。我不清楚Windows Vista在未指定
NO_SERIALIZE
时使用什么堆分配策略。从它的性能来看,它似乎使用了一些非常幼稚的东西

更多信息: 查看
allocpeed.exe的几个堆栈快照,它似乎总是处于就绪状态(未运行或等待),并且处于HeapFree的TryInterCriticalSection中,并将CPU固定在接近100%的负载下40秒。(在Windows Vista上。)

快照示例:

ntdll.dll!RtlInterlockedPushEntrySList+0xe8
ntdll.dll!RtlTryEnterCriticalSection+0x33b
kernel32.dll!HeapFree+0x14
allocspeed.EXE+0x11ad
allocspeed.EXE+0x1e15
kernel32.dll!BaseThreadInitThunk+0x12
ntdll.dll!LdrInitializeThunk+0x4d
这很奇怪,因为
NO_SERIALIZE
精确地告诉它跳过锁获取。有些东西不合逻辑


这是一个只能回答的问题:)

Oops,谢谢。末端被截断了!您可以在两个不同的操作系统中比较ntdll中RtlFreeHeap的代码,看看是否有任何明显的差异。因此,原来包含AllocPeed.exe应用程序和源代码的链接已失效。我没有后援。有人有副本吗?:)另外,您是如何分析ntdll函数的?
dumpbin/disasm ntdll.dll>dump.txt
然后是
gvim dump.txt
。看起来删除查找列表是由于可能的堆利用。请参阅并很好地找到re:lookaside List的删除。接受此答案。出色的研究亚历克斯。我认为除了微软自己,我们不会从任何人那里得到更好的答案。