在 NTFS 上打开许多小文件太慢了

Opening many small files on NTFS is way too slow

我正在编写一个程序,该程序应该处理许多小文件,例如数千甚至数百万。 我一直在 500k 文件上测试该部分,第一步只是迭代一个目录,其中包含大约 45k 目录(包括子目录的子目录等)和 500k 小文件。遍历所有目录和文件,包括获取文件大小和计算总大小大约需要6秒。现在,如果我尝试在遍历时打开每个文件并立即关闭它,它看起来好像永远不会停止。事实上,它花费的时间太长了(几个小时......)。由于我在 Windows 上执行此操作,因此我尝试使用 CreateFileW、_wfopen 和 _wopen 打开文件。我没有在文件上读取或写入任何内容,尽管在最终实现中我需要只读。但是,我在任何尝试中都没有看到明显的改进。

我想知道是否有更有效的方法来使用任何可用函数打开文件,无论是 C、C++ 还是 Windows API,或者唯一更有效的方法是读取 MFT 和直接读取磁盘块,我试图避免这种情况?

更新:我正在处理的应用程序正在使用版本控制进行备份快照。所以,它也有增量备份。 500k 文件的测试是在一个巨大的源代码存储库上完成的,以便进行版本控制,类似于 scm。因此,所有文件都不在一个目录中。还有大约 45k 个目录(如上所述)。

因此,建议的压缩文件解决方案没有帮助,因为备份完成后,所有文件都将被访问。因此,我不会从中看到任何好处,甚至会产生一些性能成本。

您可以尝试一个 hack:以低压缩比压缩这些文件,然后使用一些 Zip 库来读取它们,这通常比一个一个地读取单个文件要快得多。 当然,这应该作为预处理步骤提前完成。

您可以尝试执行一次以将文件枚举为数据结构,然后在第二次通过时打开和关闭它们,以查看交错操作是否导致争用。

正如我在评论中发布的那样,在单个 NTFS 目录中拥有大量条目会带来很多性能问题。因此,如果您可以控制这些文件在目录中的分布方式,您可能希望利用这一点。

同时检查您系统上的反恶意软件。有些会在您每次尝试访问文件时扫描整个文件,从而减慢每个文件的访问速度。使用 Sysinternals Procmon 可以帮助您发现此类问题。

在尝试提高性能时,最好设定一个目标。多快才够快?

编辑: 原始答案的这一部分不适用,除非您使用 Windows XP 或更早版本:

默认情况下,打开和关闭每个文件都会更新索引中的上次访问时间。您可以尝试关闭该功能 via registry or command line 的实验,看看它有多大的不同。我不确定在您的实际产品中这样做是否可行,因为它是一个全局设置。

您尝试做的事情对于 任何 操作系统来说本质上都很难有效地完成。 45,000 个子目录无论如何划分都需要大量的磁盘访问。

就 NTFS 而言,任何超过 1,000 字节的文件都是 "big"。如果有办法使大多数数据文件小于大约 900 字节,您可以通过将文件数据存储在 MFT 中来实现重大效率。那么获取数据的成本就不会比获取文件的时间戳或大小高。

我怀疑是否有任何方法可以优化程序的参数、进程选项,甚至操作系统的调整参数来使应用程序运行良好。除非您能以完全不同的方式重新构建它,否则您将面临多小时的操作。

一种策略是将文件分发到多台计算机(可能有数千台),并在每个进程上都有一个子应用程序处理本地文件,将任何结果提供给主应用程序。

另一种策略是将所有文件重新构建为几个更大的文件,如@felicepollano 建议的大 .zip 文件,从而有效地虚拟化您的文件集。随机访问 4000 GB 的文件本质上比访问 40 亿个 1 MB 的文件更有效地利用资源。此外,将所有数据移动到合适的数据库管理器(MySQL、SQL 服务器等)将实现此目的,并可能提供其他好处,如轻松搜索和轻松归档策略。

对于具有该数量文件的 NTFS 卷,每个文件 5 到 20 毫秒的开销并不异常。 (在传统的主轴驱动器上,无论如何你不能期望比这更好,因为它与磁头寻道时间的顺序相同。从这一点开始,我假设我们正在处理企业 -class 硬件,SSD and/or RAID。)

根据我的经验,您可以通过并行化请求显着提高吞吐量,即使用多线程 and/or 进程。大部分开销似乎是按线程计算的,系统一次打开十个文件的速度几乎与它自己打开一个文件的速度一样快。我不确定这是为什么。您可能需要进行试验以找到最佳的并行化级别。

系统管理员还可以通过将内容复制到新卷来显着提高性能,最好以与访问内容大致相同的顺序进行复制。我最近不得不这样做,它将备份时间(对于包含大约 1400 万个文件的卷)从 85 小时减少到 18 小时。

您也可以尝试 OpenFileById(),这可能对大目录中的文件执行得更好,因为它绕过了枚举目录树的需要。但是,我自己从来没有尝试过,它可能不会有太大影响,因为如果您只是枚举目录,它可能无论如何都会被缓存。

您还可以通过 reading them from the MFT 更快地枚举磁盘上的文件,尽管目前听起来这对您来说不是瓶颈。

NTFS 处理大量文件时速度较慢。特别是如果它们在同一个目录中。当它们被划分为单独的目录和子目录时,访问速度更快。我有使用摄像机板(4 个摄像机)存储的许多文件的经验,甚至看不到文件的数量和大小(根文件夹上的属性)也太慢了。有趣的是,当磁盘是 FAT32 时,同样要快得多。并且所有消息来源都说 NTFS 更快......也许读取单个文件更快,但目录操作更慢。

为什么需要这么多文件?我希望启用目录索引服务。