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

Opening many small files on NTFS is way too slow

本文关键字:文件 许多小 NTFS      更新时间:2023-10-16

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

我想知道是否有更有效的方法来打开具有任何可用功能的文件,无论是 C、C++ 还是 Windows API,或者唯一更有效的方法是读取 MFT 并直接读取磁盘块,我试图避免?

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

因此,建议的压缩文件解决方案无济于事,因为当备份完成时,就是访问所有文件的时候。因此,我看不到任何好处,甚至会产生一些性能成本。

您尝试执行的操作本质上是任何操作系统都难以有效地完成的。 45,000 个子目录需要大量磁盘访问,无论它如何切片。

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

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

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

另一种策略是将所有文件重新架构成几个较大的文件,例如@felicepollano建议的大.zip文件,从而有效地虚拟化您的文件集。 随机访问 4000 GB 文件本质上比访问 40 亿个 1 MB 文件更有效率。 此外,将所有数据移动到合适的数据库管理器(MySQL,SQL Server等)中也可以实现这一点,并且可能提供其他好处,例如易于搜索和简单的存档策略。

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

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

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

您也可以尝试 OpenFileById(),它对于大目录中的文件可能表现得更好,因为它绕过了枚举目录树的需要。 但是,我自己从未尝试过,它可能不会产生太大影响,因为如果您只是枚举了目录,则无论如何都可能会缓存该目录。

还可以通过从 MFT 读取磁盘上的文件来更快地枚举磁盘上的文件,尽管听起来这目前对你来说不是瓶颈。

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

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

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

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

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

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

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

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

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