【问题标题】:Random Complete System Unresponsiveness Running Mathematical Functions运行数学函数的随机完全系统无响应
【发布时间】:2011-01-20 03:40:17
【问题描述】:

我有一个程序,它一次加载一个文件(从 10MB 到 5GB)一个块(ReadFile),并为每个块执行一组数学运算(基本上计算哈希)。

计算哈希后,它将有关块的信息存储在 STL 映射中(基本上是<chunkID, hash>),然后将块本身写入另一个文件(WriteFile)。

就是这样。该程序将导致某些 PC 窒息和死亡。鼠标开始卡顿,任务管理器需要超过 2 分钟才能显示,ctrl+alt+del 没有响应,运行程序很慢...... 工作。

我已经尽我所能来优化程序,并对所有对象进行了三次检查。

我做了什么:

  • 尝试了不同的(强度较低的)哈希算法。
  • 将所有分配切换到 nedmalloc 而不是默认的 new 运算符
  • 从 stl::map 切换到 unordered_set,发现性能仍然很差,所以我再次切换到 Google 的 dense_hash_map。
  • 将所有对象转换为存储指向对象的指针而不是对象本身。
  • 缓存所有读写操作。我没有读取文件的 16k 块并对其执行数学运算,而是将 4MB 读入缓冲区并从 there 读取 16k 块。所有写入操作都相同 - 它们在写入磁盘之前合并为 4MB 块。
  • 使用 Visual Studio 2010、AMD Code Analyst 和 perfmon 运行广泛的分析。
  • 将线程优先级设置为 THREAD_MODE_BACKGROUND_BEGIN
  • 将线程优先级设置为 THREAD_PRIORITY_IDLE
  • 在每个循环之后添加了一个 Sleep(100) 调用。

即便如此,在某些情况下,应用程序仍会导致某些机器上的系统范围挂起。

Perfmon 和 Process Explorer 显示 CPU 使用率最低(睡眠时),磁盘没有持续的读取/写入,很少出现硬页面错误(在 5GB 输入文件上的应用程序生命周期中只有大约 30k 页面错误),虚拟内存很少(从不超过 150MB),没有泄漏的句柄,没有内存泄漏。

我测试过的机器运行 Windows XP - 包括 Windows 7、x86 和 x64 版本。没有一个内存小于 2GB,尽管在内存较低的情况下问题总是会加剧。

我不知道下一步该做什么。我不知道是什么原因造成的——罪魁祸首是 CPU 还是内存。 CPU 因为没有睡眠和在不同的线程优先级下,系统性能会发生显着变化。内存,因为在使用 unordered_set 与 Google 的 dense_hash_map 时,问题发生的频率存在巨大差异。

真正奇怪的是什么?显然,NT 内核设计应该防止这种行为发生永远(用户模式应用程序将系统驱动到这种极差的性能!?)......但是当我编译代码并在 OS X 或 Linux 上运行它(它始终是相当标准的 C++),即使在 RAM 很少且 CPU 较弱的糟糕机器上也能表现出色。

接下来我该怎么做?当所有的指标都表明应用程序本身并没有做任何极端的事情时,我怎么知道 Windows 在幕后做了什么导致系统性能下降?

欢迎任何建议。

【问题讨论】:

  • 会不会是其他软件造成的?喜欢杀毒软件?
  • 分析是否突出了任何特定于 Windows 的瓶颈?
  • +1 对于一个解释清楚的问题,有背景等。
  • @C.罗斯:谢谢。我试图尽可能详细,因为这是我曾经见过的最奇怪的问题,而且我在我的时间里遇到了一些非常奇怪的事情!
  • @Timo Geusch:不。唯一特定于 Windows 的瓶颈是 RtlEnterCriticalSection,应该忽略它。

标签: c++ windows performance optimization kernel


【解决方案1】:

需要检查的一些事项:

  • 防病毒软件。这些通常会在打开文件时扫描文件以检查病毒。您的延迟是否在应用读取任何数据之前发生?
  • 一般系统性能。使用资源管理器复制文件是否也会显示此问题?
  • 您的代码。把它分解成不同的阶段。编写一个只读取文件的程序,然后是一个读取和写入文件的程序,然后是一个只散列随机内存块的程序(即删除磁盘 IO 部分),看看是否有任何特定步骤有问题。如果你能得到一个分析器,那么也可以使用它来查看你的代码中是否有任何慢点。

编辑

更多想法。也许您的程序过多地持有 GDI 锁。这可以解释在没有高 CPU 使用率的情况下其他一切都很慢。一次只能有一个应用程序可以拥有 GDI 锁定。这是一个 GUI 应用程序,还是只是一个简单的控制台应用程序?

您还提到了 RtlEnterCriticalSection。这是一个代价高昂的操作,并且可以很容易地挂起系统,即不匹配的 Enter 和 Leaves。你是多线程的吗?速度变慢是因为线程之间的竞争条件吗?

【讨论】:

  • 您的所有观点都不适用于我的情况......整个程序的延迟是一致的(在源和目标上的 OpenFile 之后和 CloseFile 之前)。出现这个问题的系统本身并不慢,尽管正如我提到的,这个问题在慢速系统上更加严重。代码不能被分解,因为事件的顺序取决于它之前部分的结果,我什至不能伪造回复,因为需要一些真实的行为。在我原来的帖子中,我声明我使用了分析器,但没有证明有用。
  • 它实际上是一个库 - 它有一个测试 GUI 应用程序并集成到服务中以供实际使用。两者都会导致一般系统无响应。多线程是干净的,当它被实现为单线程(CriticalSection-free)库时也会出现问题。线程之间的争用会导致 我的应用程序 变慢,但不会导致操作系统变慢。关键部分是用户模式(不是内核模式)对象,不会影响 系统 性能。
【解决方案2】:

我知道您说过您已经监控了内存使用情况,并且这里似乎很少,但症状听起来很像操作系统疯狂地抖动,这肯定会导致操作系统响应能力的普遍丧失,就像您看到的那样。

当您在一个文件上运行应用程序时,比如说可用物理内存大小的 1/4 到 1/2,它似乎工作得更好吗?

我怀疑可能发生的情况是,Windows 正在“帮助”将您的磁盘读取缓存到内存中,而不是将缓存内存放弃给您的应用程序以供使用,从而迫使它进行交换。因此,即使交换使用最少(150MB),它也会在您计算哈希时不断进出。这会导致系统崩溃。

【讨论】:

  • 这有一种身临其境的感觉。
  • 这正是我最初的想法,因此我提到了页面错误。我无法知道这是否真的如此。我自己已经在缓存磁盘读/写,并且相对缓慢地通过缓冲区吃东西,以至于 Windows 不会这样做(或者这就是人们会想的!)。较小的文件意味着更少的块,这意味着 Map 中的条目更少,这意味着更少的计算,这意味着更快的查找,更空的 CPU 缓存等。基本上,仅仅因为较小的文件不会发生问题并不意味着它是操作系统交换 - 做你知道怎么知道吗?
  • +1 看看这个,当我使用 Windows 作为我的日常操作系统时,我在处理大型数据文件时会经常遇到磁盘缓存问题。很多时候,它会将我的应用程序分页到虚拟内存,以便将内存用作磁盘缓存。我相信有一些 tweeks 来调整这一点,但我不记得他们对不起什么,我使用的是 winXP,
  • 一个问题,你从来没有提到(或者我错过了)没有睡眠的 CPU 使用率是多少 - 它是 CPU 挂钩的吗?如果是这样,那么这意味着我的理论是不正确的。如果它是 5-10% 的 I/O 阻塞气味,几乎可以肯定不是直接的散列或映射操作。与磁盘 I/O 相比,我猜哈希/映射应该相对较小,因此应该有一定的文件大小,您的程序可以像复制一样快速完成。在达到一定的大小阈值后,应该会发生两件事:页面错误急剧增加和系统性能下降。
  • Mark B,初始备份的 CPU 使用率为 30%(该进程是多线程的,因此是真正的 30%),增量备份的 CPU 使用率 > 90%。 IO 开销空间不大?
【解决方案3】:

听起来你在四处寻找解决问题,却不知道问题出在哪里。 Take stackshots. 当问题发生时,他们会告诉你你的程序在做什么。如果问题发生在您无法使用 IDE 或堆栈采样器的其他机器上,则获取堆栈快照可能并不容易。一种可能性是杀死应用程序并在它运行时获取堆栈转储。您需要在可以获得堆栈转储的环境中重现该问题。


补充:你说它在 OSX 和 Linux 上表现良好,而在 Windows 上表现不佳。我假设完成时间的比率是一个相当大的数字,比如 10 或 100,如果你甚至有耐心等待它的话。我在评论中说过,但这是一个关键点。该程序正在等待某些东西,您需要找出什么。它可能是人们提到的任何事物,但它不是随机的

每个程序在运行时都有一个调用堆栈,该堆栈由特定地址的调用指令层次结构组成。如果在某个时间点它正在计算,则堆栈上的最后一条指令是非调用指令。如果它在 I/O 中,堆栈可能会到达您看不到的几级库调用。没关系。堆栈上的每条调用指令都在等待。它正在等待它请求完成的工作。如果您查看调用堆栈,并查看调用指令在您的代码中的位置,您就会知道您的程序在等待什么。

您的程序,因为它需要 很长时间 才能完成,所以 几乎所有 时间都在等待某事完成,正如我所说,这就是你需要找出来。在运行缓慢时获取堆栈转储,它会给您答案。它错过它的机会是 1/the-slowness-ratio。

很抱歉,这个问题太简单了,但是很多人(和分析器制造商不明白。他们认为他们必须衡量。

【讨论】:

  • 我认为这不会有帮助。正如我所提到的,我已经在 VS2010 和 CodeAnalyst 下进行了广泛的分析,这两者都采用了你所说的“stackshots”。它们完全符合我的预期:CPU 大部分时间都在计算哈希,一些时间在 STL 映射中查找条目,一些时间进入关键部分,还有一些时间读/写。人们在这里描述的所有方法都是分析应用程序的减速。但是我知道为什么 my 程序很慢,但我想知道它为什么会减慢 system 的速度。
  • @Computer-Guru:Stackshots 是不同的。 VS2010(我上次查看)在 I/O 期间关闭采样,所以如果问题发生在 I/O 期间它不会看到它。 CA 看起来可能相似。您的问题(我敢打赌)是 I/O,因此 CPU 花费时间的地方无关紧要。您想知道的是,当它被卡住时,调用堆栈上有哪些特定的代码行等待它完成它无法完成的事情。如果您的应用需要更长的时间才能完成,那么即使您认为问题出在其他地方,1 个示例也应该说明原因。
  • 但是,迈克,您说“当它卡住时”,除了应用程序不会卡住 - Windows 会卡住。我的问题是,当原因在应用程序中并且结果在Windows上时,我不知道如何衡量因果关系?
  • @Computer-Guru:我理解你的沮丧,但我做过很多这样的案例,“测量”的想法阻碍了。当你没有这个问题时,程序会在一段时间内完成,比如 1 分钟。当您遇到问题时,它会在 60 分钟内完成。这意味着每 60 个挂钟毫秒中有 59 个处于挂起状态,等待它要求完成的事情。因此,单个堆栈样本(在随机挂钟时间,而不是 CPU 时间)将详细显示您的程序中的什么使 Windows 疯狂,只有 1/60 的机会错过它。帮忙?
  • ... 换句话说,程序总是在等待一些东西。 (如果不是,它就会完成。)我们知道它的实际 CPU 工作需要 1 分钟。所以在剩下的 59 分钟里,它到底在等什么?。不用担心测量,只需用一个堆栈样本问这个简单的问题。
【解决方案4】:

XPerf 是您的指南 - 观看 PDC Video 的相关信息,然后追踪行为不端的应用程序。它会准确地告诉你整个系统发生了什么,它非常强大。

【讨论】:

    【解决方案5】:

    我喜欢磁盘缓存/抖动建议,但如果不是这样,这里有一些零散的建议:

    您要链接到哪些非 MSVC 库(如果有)?

    可以修改您的程序(#ifdef'd)以在没有 GUI 的情况下运行吗?有问题吗?

    您在每个线程的每个循环之后添加了 ::Sleep(100),对吗?你在说多少线程?几个还是几百个?每个循环大概需要多长时间?如果你这样做 ::Sleep(10000) 会发生什么?

    您的程序是否可能正在执行其他锁定有限资源的操作(ProcExp 可以向您显示正在获取哪些句柄...当然您可能会遇到 ProcExp 没有响应的困难:-[)

    你确定 CriticalSections 是用户态的吗?我记得当我在 Windows 上工作时(或者我相信),那是很早以前的事了,但微软本可以修改它。我在 MSDN 文章 Critical Section Objects (http://msdn.microsoft.com/en-us/library/ms682530%28VS.85%29.aspx) 中没有看到任何保证......这让我想知道:Anti-convoy locks in Windows Server 2003 SP1 and Windows Vista

    嗯...大概我们现在都是多处理器了,所以你是在 CS 上设置自旋计数吗?

    如何运行这些操作系统之一的调试版本并监视内核调试输出(使用 DbgView)...可能使用 Platform SDK 中的内核调试器...如果 MS 仍然调用它?

    我想知道 VMMap(另一个 SysInternal/MS 实用程序)是否有助于磁盘缓存假设。

    【讨论】:

      【解决方案6】:

      事实证明,这是 Visual Studio 编译器中的一个错误。使用不同的编译器可以完全解决这个问题。

      就我而言,我安装并使用了英特尔 C++ 编译器,即使禁用了所有优化,我也没有看到我在这个库上使用 Visual Studio 2005 - 2010 编译器时遇到的全系统挂起。

      我不确定是什么导致编译器生成这种损坏的代码,但看起来我们将购买英特尔编译器的副本。

      【讨论】:

        猜你喜欢
        • 2012-11-20
        • 2019-07-12
        • 1970-01-01
        • 2015-12-20
        • 2013-04-03
        • 2016-10-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多