【译】调查并确定 Windows 运行速度变慢问题

news/2025/1/21 9:47:45/文章来源:https://www.cnblogs.com/frendguo/p/18682996

我最近注意到,我的 Windows 7 家用笔记本电脑运行速度比平时要慢。系统似乎在磁盘 I/O 上花了很多时间。当我查看任务管理器时,我发现笔记本电脑的缓存磁盘数据量大约只有平时的 五分之一,但问题的原因并不明显。

我的工作之一是性能专家——要调查各种日常或深层次的性能问题。所以当我意识到我的笔记本出现了严重且系统性的问题时,我集中精力,拿出所有专业工具进行调查,最终找到了罪魁祸首。

你即将读到的这个故事完全真实。唯一被改动的是一些缓存、可用和空闲内存的精确数值,以保护“无辜者”。

提前剧透:我从 Costco 买的那台 Western Digital My Book Live 备份驱动器是罪魁祸首。

再多剧透一点:在我发表这篇文章九天之后,Western Digital 联系了我,并非常认真地对待这个问题。大约四个月后,他们发布了修复补丁。这个故事的结局很美好,而且你可以通过博客来改变世界。


缓存的意义

想要理解这个谜团,首先得理解 Windows 磁盘缓存的重要性。在计算机里拥有大量内存是很重要的,不仅能让你同时运行很多程序,而不必动用页面文件,而且还能让更多的数据缓存到内存里。硬盘由于依赖物理旋转盘片,是计算机里速度最慢的组件之一,而笔记本硬盘则是硬盘中最慢的类型。Windows 会把硬盘中的数据缓存到内存中,以便能瞬间访问这些数据,而不是每次都要花上几毫秒来读取。缓存的数据越多,你对磁盘的访问就越少,计算机速度也就越快。正因为如此,Windows 的磁盘缓存几乎可以说“内存越多越好”。我工作的机器有 32 GiB(GiB 指二进制 1024 的幂的单位),这意味着我通常能缓存超过 24 GiB 的磁盘数据,这让我能在不同的程序和不同的项目之间切换时,最大程度地减少磁盘 I/O。

从 Windows Vista 开始,Windows 引入了一些只为将磁盘缓存填满而生的系统机制,它会根据日志判断你下一步最可能需要哪些数据,然后提前加载。比如 SuperFetch 会监控你的使用模式,在你启动应用程序时提前预取数据。系统空闲时也会进行预取。

这些功能在 Windows Vista 刚推出时让人们感到困惑。用户抱怨 Vista 占用大量内存,很“笨重”,因为在任务管理器里看到几乎没有空闲内存。然而事实证明,这其实是好事。空闲内存完全是浪费,一点用都没有。一台运行良好的 Windows 机器应该几乎没有空闲内存,而有大量被用来做磁盘缓存的内存。缓存的内存是有用的,因为它能让系统运行得更快,而且当需要时,缓存内存可以随时转变成空闲内存来供程序分配。


健康的机器

当 Windows 正常运行时,任务管理器(通过右键点击任务栏来打开)在物理内存统计信息中应该显示类似右图的内容:大量的 cached(缓存)内存,大量的 available(可用)内存,以及很少的 free(空闲)内存。如果 available 内存很低(低于几千 MiB),就值得考虑增加更多内存,以便 Windows 有更多空间来运行程序并缓存数据。如果 free 内存很高(高于几百 MiB),那么要么 Windows 还没来得及往磁盘缓存中填充数据,要么就是哪里出了问题。


不健康的机器

在我的笔记本上,任务管理器的“性能”选项卡看起来是这样的:

上图中紫色圈出的区域显示:我有很多可用内存,但磁盘缓存比预期要小(对于一台 8 GiB 的机器来说),而且空闲内存明显偏高。我的 8 GiB 笔记本通常能缓存 4-5 GiB 的数据,但我注意到问题发生时,它只能缓存 1-2 GiB 左右的数据。

查看内存使用历史,则能看到为什么缓存内存低、空闲内存高。

上图中红色圈起的部分显示了三次短暂的内存使用峰值。每一处峰值都对应了一个瞬时的 4 GiB 内存分配。Windows 为满足其中一次分配,把 4 GiB 有用数据从文件缓存中腾空,最后就变成了浪费的空闲内存。

任务管理器帮助我了解了问题的大概结构,但并不能找到罪魁祸首。它的“峰值工作集(Peak Working Set)”一列里没有显示任何可能导致这类峰值的进程。这时候我就要使用最喜欢的系统分析工具了。


用 xperf 进行调查

我使用了我所知最强大的性能调查工具:ETW tracing(也就是 xperf),来自 Windows Performance Toolkit(WPT)。这是微软免费提供的一套工具,可以从 CPU 使用率、磁盘 I/O、内存分配等各个角度来分析 Windows 的性能。xperf 使用起来并不容易,但对于熟练用户来说,它能提供对 Windows 性能几乎无限的洞察力。

我决定使用新的 Windows Performance Recorder UI(wprui)来记录追踪数据。我勾选了所有和内存分配相关的选项,并让它把日志记录到内存中(目前我会使用 UIforETW,在“Settings”对话框中勾选“VirtualAlloc stacks always”,在“Extra kernel flags”里选“POOL”,并在“Extra kernel stackwalks”里选“PoolAlloc”)。在这种模式下,wprui 或 UIforETW 会持续监控你的系统,将详细的性能信息写入到内存中的循环缓冲区里。当你发现性能问题时,点击“Save”按钮,就能把缓冲区保存到磁盘上。也就是说,它是一种“事后”分析器,你可以随时记录最后约 30 秒的系统活动。我一般会让这种内存记录模式一直开着,这样我随时可以捕捉罕见的性能问题,而不用去想如何复现。当下图截图时,我已经让 wprui 连续运行了快一周,但它的“Time”字段每天会重置。


抓个正着

内存峰值大多出现在随机时刻,很容易错过,所以我一直没能捕捉到想要的追踪记录。不过我最后发现,每次我的笔记本从待机恢复时,都会出现一次内存峰值。这样一来就简单多了。只要在 wprui 开始记录后,让笔记本休眠并恢复,登录后立刻点击“Save”就行。搞定!然后我把记录加载到 WPA(Windows Performance Analyzer)里,查看和内存相关的异常。这一步有时候需要耐心,因为你并不知道应该看哪里,也不知道具体要找什么,但有时当你看到正确的图表时,问题会非常明显。看看下面这张图,能看到那个极其明显的内存峰值吗?

到目前为止,我们看到的还只是跟任务管理器类似的信息,但 xperf 的强大之处在于可以深入得多。这个追踪记录包含所有池(pool)内存分配事件的调用栈。只要选中合适的时间范围(从峰值中段到峰值前),我们就能得到一个汇总表。通过合理设置汇总表列,按大小排序,再深入到 AIFO 类型(即“在选定时间范围内分配,但未在范围内释放”),我们很快就找到了那个导致问题的 4,294,967,296 字节的分配(也就是 4 GiB)。而且我们还拿到了调用栈。

最初看到这个调用栈时,我以为是 Windows 的 bug。因为我最近才装了一些 Windows 更新,我猜可能是哪个补丁导致了这个问题。结果我猜错了,但需要再深入一些才能明白。

从调用栈可以看到,FstubAllocateDiskInformation 这个函数做了两次 ExAllocatePoolWithTag 调用,其中一次分配了大量内存。现在是时候看看 FstubAllocateDiskInformation 做了什么。


真相在代码里

要查看 FstubAllocateDiskInformation 这段代码,最简单的方法是使用 Local Kernel Debugging。通过它可以让 windbg 获取内核的内存快照,而无需真正中断内核执行。它对调试功能有局限,比如不能设置断点或者单步调试,但查看反汇编已经够用了,而且比在两台机器之间用电缆做实时内核调试更简单。

很显然,这里需要用 uf 命令来反汇编此函数。下面是反汇编后的结果,我稍作了格式化,并用粗体标出了几个关键的指令,并加上了一些注释:

lkd> uf ntkrnlmp!FstubAllocateDiskInformation
nt!FstubAllocateDiskInformation:
mov qword ptr [rsp+8],rbx
mov qword ptr [rsp+10h],rbp
mov qword ptr [rsp+18h],rsi
push rdi
sub rsp,20h
mov rsi,rdx
mov rbp,rcx
mov r8d,42747346h ; pool tag of FstB
mov edx,40h ; allocation size
xor ecx,ecx ; flags
call nt!ExAllocatePoolWithTag
test rax,rax
je Exit1nt!FstubAllocateDiskInformation+0x35:
lea rdx,[rax+10h]
mov rcx,rbp
mov rbx,rax
call nt!FstubGetDiskGeometry
test eax,eax
js Exit2nt!FstubAllocateDiskInformation+0x4c:
mov ecx,dword ptr [rbx+24h]
test ecx,ecx ; compare SectorSize to zero
je Exit3nt!FstubAllocateDiskInformation+0x57:
mov rax,qword ptr [rbx+28h]
test rax,rax
je Exit3nt!FstubAllocateDiskInformation+0x64:
xor edx,edx
mov dword ptr [rbx+8],ecx
mov r8d,42747346h ; pool tag of FstB
div rax,rcx
mov rdx,rcx ; SectorSize/allocation size
mov ecx,4 ; flags
mov qword ptr [rbx+38h],rax
mov qword ptr [rbx],rbp
call nt!ExAllocatePoolWithTag
test rax,rax
je Exit4

你不需要仔细理解所有代码才能跟上后面的逻辑。代码中有两次调用 ExAllocatePoolWithTag,一次是 0x40 字节,另一次则是可变大小。两次调用都使用了 pool tag 42747346h(对应 “FstB”)。要做完整分析还要查看输入参数类型及其布局,我也去 reactos 网站上看了他们的源码版本,以确认我对这段汇编的解读没错。关键行可以这样概括:

FstubGetDiskGeometry(DiskInformation); 
...
if (DiskInformation->SectorSize != 0) ExAllocatePoolWithTag(DiskInformation->SectorSize);

换句话说,FstubAllocateDiskInformation 调用了 FstubGetDiskGeometry(参见 FstubGetDiskGeometry),然后如果 SectorSize 不为 0,就分配 SectorSize 字节的内存。

我稍微停顿了一下,去 验证 主硬盘的扇区大小:

C:>fsutil fsinfo ntfsinfo c: 
NTFS Volume Serial Number : 0x00b8457cb8457168 
Version : 3.1 
Number Sectors : 0x0000000048ad8ae2 
Total Clusters : 0x000000000915b15c 
Free Clusters : 0x00000000016ac4a5 
Total Reserved : 0x0000000000000780 
Bytes Per Sector : 512 
Bytes Per Physical Sector : 512 
...

随后,我猛地醒悟到几周前,我购买并使用了一个备份驱动器,当我第一次使用它时,它安装了一个驱动。这个驱动正是导致所有问题的根源。这正好和我开始出现性能问题的时间相吻合,也和我安装那些 Windows 更新的时间差不多。


问题解决!

我打开设备管理器,找到了问题驱动,把它卸载,然后甚至不用重启电脑,我的笔记本就恢复正常了。无论是休眠还是恢复,都不再出现内存峰值,我的磁盘缓存也不会再被清空,倍感欣慰。现在我这台笔记本有 8 GiB 内存的真正表现了,而不再像只有 4 GiB 那样。

我并不是驱动开发专家——我的领域在用户态——但我能想象大概发生了什么。这个驱动好像在处理 IOCTL_DISK_GET_DRIVE_GEOMETRY 请求时,要返回一个 DISK_GEOMETRY 结构。但如果某些信息不可用,该怎么做呢?或许可以返回一个错误代码,也可能填一个全是零的结构,但这个驱动却把所有字段——包括 BytesPerSector——都设成了 0xFFFFFFFF。由于 SectorSize 是无符号数,加上内存分配会向上取整,所以最终导致分配了 4 GiB 内存。这个错误不算太难犯,但 Western Digital 的开发者们确实应该注意到它带来的严重问题。

Windows 内核只检查了 BytesPerSector 是否等于 0,却没有检查它是否在某个合理的范围(也可参见 KB2510009)内。也就是说,内核少了一道保护,否则本可以避免此问题。

如果你拥有这台设备,我建议你把它退掉。不过,这可能还不够。我在离那个备份驱动器很远、并且没有连接它的情况下,也会出现这个问题。所以很可能需要打开设备管理器,手动卸载驱动。这个我也不完全肯定。

我还建议你联系 Western Digital,要求他们放出修复补丁。


后记

发现问题后,我原本只想把这个驱动器退掉,但在那之前,我特意确保我机器上装了最新的驱动,以及 NAS 固件上的所有更新,以保证测试结果准确。随后我清空了 NAS 里的数据,并格式化了硬盘,“Full Factory Restore” 选项会安全地擦除磁盘,这对于退回一个曾经存过我整机备份的硬盘至关重要。然后,我卸载了驱动,确认问题解决。

再一次,ETW 让我用科学的方式调查并解决了一个神秘问题。我希望能有更多的开发者运用“基于证据的方法”来调查性能问题,而不是依靠猜测。

我同情那些普通消费者,他们可能会很随意地买下这个设备,也不太可能想到把性能问题和新买的备份硬盘联系起来。

我通过一些私人渠道联系了微软,他们确认相关团队已经知晓这个问题。我在 2012 年 8 月 29 日通过普通的支持渠道联系了 Western Digital,但他们一直没回复——后来他们的一位工程师看到了这篇文章,并在 2012 年 9 月 7 日(也就是九天之后)通过技术支持渠道回复了我。


其他故事

如果你喜欢这篇文章,也许你也会喜欢我用 xperf 进行的其他取证式性能调查:

  1. Xperf and Visual Studio: The Case of the Breakpoint Hangs
  2. PowerPoint Poor Performance Problem(以及 Xperf for Excess CPU Consumption 这篇更详细的教程,它探讨了同一个问题)
  3. Faster Fractals Through Better Scheduling —— 调查并优化 Fractal eXtreme 中的一些低效点
  4. Xperf Wait Analysis–Finding Idle Time —— 分析 Visual Studio 中三秒卡顿的教程
  5. Windows Live Photo Gallery—Poor Performance Peculiarities —— 调查并解决放大图像时出现的短暂卡顿
  6. Fixing another Photo Gallery performance bug —— 调查并解决启动时的卡顿问题

如果你想学着自己用 xperf 做调查,可参考 ETW central。

关于 brucedawson

我是 Google 的一名程序员,主要工作是做优化和可靠性开发。让代码跑得快 10 倍比什么都开心,除非能同时消灭大量 bug。我也玩单轮车、打冰球(ice hockey)和轮椅冰球(sled hockey),还会玩杂耍(juggle)。有时我会纠结这个博客究竟该不该叫 randomutf-8。了解更多可看我在 Twitter 上的2010 年代回顾。

以上翻译仅供参考,原文信息请以英文原版为准。

原文链接:https://randomascii.wordpress.com/2012/09/04/windows-slowdown-investigated-and-identified/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/872673.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQLServer2005恢复Master库.110509

master库对于SQLServer来说,是很重要的系统数据库,保存着所有Sqlserver的用户信息、数据库信息等,当数据库崩溃时,master数据库的恢复成功与否起着重要的作用。这就跟Oracle的System表空间一样,非常的重要。 备份数据前期准备: (1)在备用机准备好和生产机器一样的sql20…

legged-robot关于locomotion、Navigation任务主要文章速览

0. 前言 目前legged robot包括locomotion(怎么走)、navigation(往哪走)、人形机器人的whole body control以及基于机械臂的manipulation的任务。 本文章特此记录 一方面便于日后自己的温故学习,另一方面也便于大家的学习和交流。 如有不对之处,欢迎评论区指出错误,你我共…

IT部门管理之IT十二条令.180409

团队越来越大,靠人管几乎有力无心,只能靠制度管理了。 前段时间对部门颁布了12条令,效果明显。 特此Mark。汇报:三条总结:汇报讲究精简,一个事情最多一句话概括。 一页报告:内容精简,报告一页word搞定。 统计分析:尽量用报表或图表说话。用数字说话,比用语言表达更加…

IDEA如何查看类中的方法和属性列表

前言 大家好,我是小徐啊。我们在开发Java应用的时候,一般都是需要写属性和方法的。这些属性和方法构成了我们代码开发的基础。当一个Java类中,有很多属性和方法的时候,如何快速找到这些方法和属性呢?今天,小徐就来介绍下如何在IDEA中查找。 如何查找属性和方法 首先,打开…

MySql 执行计划

目录获取执行计划解读执行计划type 字段Extra 字段访问谓词与过滤谓词完整字段信息格式化参数执行计划中的分区表信息获取额外的执行计划信息获取指定连接的执行计划获取实际运行的执行计划执行计划(execution plan,也叫查询计划或者解释计划)是 MySQL 服务器执行 SQL 语句的…

用css实现倒影的效果

在前端开发中,使用 CSS 来实现倒影效果是一个常见的技巧。这通常可以通过使用 CSS3 的 box-reflect 属性或者通过伪元素和线性渐变来实现。然而,需要注意的是,box-reflect 是一个非标准的 WebKit 属性,主要在 Chrome 和 Safari 中支持,而在 Firefox 和 IE 中不支持。 方法…

Mac电脑如何卸载软件?App Cleaner

Mac电脑如何卸载软件?App Cleaner 垃圾清理 App Cleaner & Uninstaller Pro Mac,是一款Mac卸载工具,残余垃圾清除工具!可以卸载应用程序或只删除不需要的服务文件,甚至可以删除以前删除的应用程序中的文件。使用该应用程序,您可以管理Mac扩展程序并使用一个按钮禁用所…

20 GitHub 仓库帮助你成为 React专家

原文:https://dev.to/martinadamsdev/20-github-repositories-to-become-a-react-master-opl#how-to-become-a-react-master-1推荐的 GitHub 仓库列表:reactjs/reactjs.org:官方 React 文档,提供了详细的学习指南和示例代码。facebook/react:React 的官方仓库,包含了核心…

WebStorm 2024.3.1 前端开发工具

WebStorm 2024.3.1 前端开发工具 JetBrains WebStorm 2024 mac,是一款JavaScript开发工具,WebStorm 非常了解您的项目结构,可以在编码的各个方面提供帮助。它将自动补全代码,检测错误和冗余并提出修正建议,帮助您安全地重构代码。

MySQL数据库开启远程访问权限

1、背景描述 默认情况下,MySQL 只允许本地登录,即只能在安装 MySQL 数据库所在的主机环境中访问。 在实际开发和使用中,一般需要访问远程服务器的数据库,此时就需要开启服务器端 MySQL 的远程访问权限。 2、查看MySQL的用户表如上图所示,Host 列指定了允许用户登录所使用的…

如何高效且安全地进行网站页面模板的修改?

在现代网站建设中,页面模板的修改是提升用户体验、优化SEO排名以及保持网站美观度的重要手段。以下是详细的步骤:备份现有模板:在开始任何修改之前,请确保已经对当前使用的模板进行了完整备份。这可以防止意外错误导致的数据丢失。 选择合适的编辑工具:根据所用的内容管理…