代码之家  ›  专栏  ›  技术社区  ›  wojo

Windows Vista和Windows 7上使用heap_no_serialize的堆内存函数出现~100x减速的原因

  •  11
  • wojo  · 技术社区  · 16 年前

    我试图追踪WindowsVista和Windows7中堆内存函数的巨大减速(我没有在任何服务器版本上测试)。它根本不在WindowsXP上发生,只在微软更新的操作系统上发生。

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

    我追溯到国旗的使用 HEAP_NO_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_memory_manager :

    #ifdef ZEND_WIN32
        AG(memory_heap) = HeapCreate(HEAP_NO_SERIALIZE, 256*1024, 0);
    #endif
    

    我删除了 堆没有序列化 参数(替换为0),解决了问题。脚本现在可以在cli和sapi apache 2版本中快速清理。这是针对php 4.4.9的,但是php 5和6源代码(在开发中)在调用中包含相同的标志。

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

    为什么Windows Vista和Windows 7上的堆内存功能如此缓慢? 堆没有序列化 ?

    在研究这个问题的时候,我想出了一个好主意。请阅读博客文章 http://www.brainfarter.net/?p=69 海报解释了这个问题,并提供了一个测试用例(源代码和二进制代码都可用)来突出显示这个问题。

    我在Windows7x64四核8 GB计算机上的测试给出了 四万三千八百三十六 . 哎哟!同样的结果没有 堆没有序列化 旗是 六百五十五 在我的情况下,快了大约70倍。

    最后,似乎使用Visual C++ 6创建的任何程序都可以使用。 malloc / free 或 new / delete 似乎受到了这些新平台的影响。Visual C++ 2008编译器默认不会为这些函数/操作符设置此标志,因此它们不会受到影响,但这仍然会影响很多程序!

    我鼓励你下载概念证明并尝试一下。这个问题解释了为什么我在Windows上安装的普通PHP会爬行,也可能解释了为什么Windows Vista和Windows 7有时看起来会变慢。

    更新2010-01-26: 我收到微软的回复,说 low-fragmentation heap (lfh)是拥有任何可观数量分配的堆的事实上的默认策略。在WindowsVista中,他们重新组织了大量代码以删除额外的数据结构和代码路径,这些不再是处理堆API调用的常见情况的一部分。与 堆没有序列化 标记和在某些调试情况下,它们不允许使用LFH,我们会被困在通过堆管理器的缓慢且不太优化的路径上。所以…强烈建议不要使用 堆没有序列化 因为您将错过所有的工作,以LFH和任何未来的工作在Windows堆API。

    1 回复  |  直到 13 年前
        1
  •  9
  •   Peter Mortensen Pieter Jan Bonestroo    13 年前

    我注意到的第一个区别是Windows Vista 总是 使用 低分片堆 (LFH)。Windows XP似乎没有。 RtlFreeHeap 因此,在Windows Vista中,所有工作都委托给了 RtlpLowFragHeapFree . More information regarding LFH 以及它在各种操作系统中的存在。注意顶部的红色警告。

    More information (备注部分):

    Windows XP、Windows Server 2003和 具有修补程序kb 816542的Windows 2000:

    旁观者名单是一种快速记忆。 包含 只有固定大小的块。旁观 默认情况下为堆启用列表 支持他们。 从开始 Windows Vista,查找列表 未使用且LFH由启用 违约 .

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

    从开始 Windows Vista, 旁观名单是 不使用

    这意味着设置 诺西序列化 在Windows Vista中禁用LFH,但它 不 (并且不能)回到标准的旁观者名单(作为一个快速的替代品),根据上述报价。我不清楚Windows Vista使用什么堆分配策略 诺西序列化 指定。根据它的性能,它似乎在使用一些非常幼稚的东西。

    更多信息:

    查看一些堆栈快照 allocspeed.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
    

    这很奇怪,因为 诺西序列化 准确地告诉它 跳过 锁定采集。有些事不对劲。

    这只是个问题 Raymond Chen 或 Mark Russinovich 可以回答: