在 NTFS 上打开许多小文件太慢了
Posted
技术标签:
【中文标题】在 NTFS 上打开许多小文件太慢了【英文标题】:Opening many small files on NTFS is way too slow 【发布时间】:2015-03-06 20:34:00 【问题描述】:我正在编写一个程序,它应该处理许多小文件,比如数千甚至数百万。 我一直在对 500k 文件测试该部分,第一步只是迭代一个目录,其中包含大约 45k 目录(包括子目录的子目录等)和 500k 小文件。遍历所有目录和文件,包括获取文件大小和计算总大小大约需要 6 秒。现在,如果我尝试在遍历时打开每个文件并立即关闭它,它看起来就像它永远不会停止。事实上,它需要的时间太长(几个小时......)。由于我在 Windows 上执行此操作,因此我尝试使用 CreateFileW、_wfopen 和 _wopen 打开文件。我没有在文件上读取或写入任何内容,尽管在最终实现中我只需要读取。但是,我在任何尝试中都没有看到明显的改进。
我想知道是否有更有效的方法来使用任何可用函数打开文件,无论是 C、C++ 还是 Windows API,或者唯一更有效的方法是读取 MFT 并直接读取磁盘块,我想避免什么?
更新:我正在处理的应用程序正在使用版本控制进行备份快照。因此,它也有增量备份。 500k 文件的测试是在一个巨大的源代码存储库上完成的,以便进行版本控制,类似于 scm。因此,所有文件都不在一个目录中。还有大约 45k 个目录(如上所述)。
因此,压缩文件的建议解决方案没有帮助,因为备份完成后,所有文件都可以访问。因此,我认为这样做没有任何好处,甚至会产生一些性能成本。
【问题讨论】:
这个问答有帮助吗? how to make createfile as fast as possible 我在 SSD 上执行此操作。问题在于打开/关闭文件 显示您的代码。没有看到你的代码。完全有可能您的代码处于无限循环中,调用了错误的 API,或者可能运行良好。但是没有你的代码,每一个建议都只是一个猜想或假设。此外,500,000 个文件是很多文件,我希望这是一个非常耗时的操作。 你真正想做什么? 代码没问题。它不进入递归,并完成(尽管经过很长时间)。它使用 FindFirstFile/FindNextFile 来遍历文件/目录。我只是在做一个基准测试,结果发现每个文件打开/关闭大约需要 5 毫秒。这就是我正在努力改进的...... @wallyk:KB2539403 说“当单个文件夹包含大量文件(超过 50,000 个文件)时,枚举文件列表时可能会出现性能问题。......当应用程序枚举目录内容时对于大型文件夹,NTFS 和缓存管理器的任务是读取和处理大量元数据以执行枚举。”是的,这绝对是关于包含大量文件的单个文件夹。 【参考方案1】:您尝试做的事情对于任何操作系统来说本质上是难以有效地完成的。 45,000 个子目录无论如何切片都需要大量磁盘访问。
就 NTFS 而言,任何超过 1,000 字节的文件都是“大”的。如果有办法使大多数数据文件小于大约 900 字节,您可以通过将文件数据存储在 MFT 中来实现主要效率。那么获取数据不会比获取文件的时间戳或大小更昂贵。
我怀疑是否有任何方法可以优化程序的参数、进程选项,甚至操作系统的调优参数,以使应用程序运行良好。除非您能以完全不同的方式重新构建它,否则您将面临数小时的操作。
一种策略是将文件分布在多台计算机(可能有数千台计算机)上,并在每个处理本地文件时都有一个子应用程序,将任何结果提供给主应用程序。
另一种策略是将所有文件重新架构成几个更大的文件,例如@felicepollano 建议的大 .zip 文件,从而有效地虚拟化您的文件集。与访问 40 亿个 1 MB 文件相比,随机访问 4000 GB 文件本质上更有效地利用资源。将所有数据移动到合适的数据库管理器(mysql、SQL Server 等)也可以实现这一点,并可能提供其他好处,例如简单的搜索和简单的归档策略。
【讨论】:
问题中的“500k”是指文件的数量,而不是文件的大小。 @AdrianMcCarthy:谢谢,我读了两遍还是错了。我已经更新了我的答案。【参考方案2】:NTFS 处理大量文件时速度较慢。特别是如果它们在同一个目录中。当它们被分成单独的目录和子目录时,访问速度更快。我有许多由摄像机板(4 个摄像机)存储的文件的经验,甚至看不到文件的数量和大小(根文件夹上的属性)也太慢了。有趣的是,当磁盘是 FAT32 时,同样的速度要快得多。而且所有消息来源都说 NTFS 更快……也许读取单个文件更快,但目录操作更慢。
为什么需要这么多文件?我希望启用目录索引服务。
【讨论】:
【参考方案3】:对于具有该文件数量的 NTFS 卷,每个文件 5 到 20 毫秒的开销并不是异常的。 (在传统的主轴驱动器上,无论如何您都不能期望比这更好,因为它与磁头寻道时间的顺序相同。从现在开始,我假设我们正在处理企业级硬件 SSD和/或 RAID。)
根据我的经验,您可以通过并行化请求(即使用多个线程和/或进程)来显着提高吞吐量。大多数开销似乎是每个线程的,系统一次打开十个文件的速度几乎与它自己打开一个文件的速度一样快。我不确定这是为什么。您可能需要进行试验以找到最佳的并行化水平。
系统管理员还可以通过将内容复制到新卷来显着提高性能,最好以与访问它们大致相同的顺序。我最近不得不这样做,它将备份时间(对于包含大约 1400 万个文件的卷)从 85 小时减少到 18 小时。
您也可以尝试OpenFileById(),这对于大目录中的文件可能会表现得更好,因为它绕过了枚举目录树的需要。但是,我自己从未尝试过,它可能不会产生太大影响,因为如果您只是枚举它,无论如何该目录可能会被缓存。
您还可以通过reading them from the MFT 更快地枚举磁盘上的文件,尽管这听起来好像目前对您来说不是瓶颈。
【讨论】:
【参考方案4】:您可以尝试将文件枚举到数据结构中,然后在第二遍中打开和关闭它们,以查看交错操作是否会导致争用。
正如我在 cmets 中所发布的,在单个 NTFS 目录中拥有大量条目会带来很多性能问题。因此,如果您可以控制这些文件在目录之间的分布方式,您可能希望利用这一点。
同时检查您系统上的反恶意软件。有些会在您每次尝试访问时扫描整个文件,从而减慢每次文件访问的速度。使用 Sysinternals Procmon 可以帮助您发现此类问题。
在尝试提高性能时,最好设定一个目标。多快才够快?
编辑:这部分原始答案不适用,除非您使用的是 Windows XP 或更早版本:
默认情况下,打开和关闭每个文件都会更新索引中的上次访问时间。您可以尝试一个实验,关闭该功能via registry 或command line,看看它有多大的不同。我不确定在您的实际产品中是否可行,因为这是一个全局设置。
【讨论】:
我在原帖中添加了一些说明。至于“速度有多快足够快”,我会说将现在花费的时间减少到五分之一(每个文件 1 毫秒或更少)是可以接受的。正如我所提到的,我可以直接使用 MFT。我只是想尽可能避免这种情况 在现代版本的 Windows 中,Last-access 默认是关闭的。 (我认为自 Vista 以来。) @HarryJohnston:你是对的。我以为在 Windows 8 中默认禁用它,但实际上是 Vista。 我认为 XP 是第一个提供禁用上次访问更新选项的版本。默认情况下,它以这样一种方式缓存,即它不会每小时写入一次以上的最后访问时间戳(可以更改为立即更新)。【参考方案5】:您可以尝试一种技巧:以低压缩率压缩这些文件,然后使用一些 Zip 库来读取它们,这通常比逐个读取单个文件要快得多。 当然,这应该作为预处理步骤提前完成。
【讨论】:
当然,zip 进程本身必须枚举和打开和关闭每个文件,所以除非 Amy 需要多次处理相同的文件,否则我不知道这会是什么更快——你仍然需要付出代价。 @AdrianMcCarthy 对于 zip 文件,只有一个“OS 文件”可以打开,并且单独的提取完全在用户空间中,绕过任何相关的内核打开/关闭处理开销或目录枚举。因此,如果zip 文件本身可以被有效地列出/查找(并使用 STORE 存储数据),那么它可能会在给定的场景中得到回报。但我想看看测试:) @AdrianMcCarthy 假设 zip 会提前生成,这个过程会被多次完成(或者 zip 生成为一些后台/夜间/关闭时间进程),但如果不是.. @user2864740:Amy 将该应用程序描述为备份应用程序,因此似乎每个文件都必须仅访问一次,因此预处理步骤似乎不是一个胜利。 您可能会尝试拥有更少的文件(但更大的文件)。您是否考虑将数据存储在某个 sqlite 数据库中?或者使用一些像GDBM这样的索引文件?以上是关于在 NTFS 上打开许多小文件太慢了的主要内容,如果未能解决你的问题,请参考以下文章