【问题标题】:What is the fastest way for reading huge files in Delphi?在 Delphi 中读取大文件的最快方法是什么?
【发布时间】:2011-01-06 13:19:25
【问题描述】:

我的程序需要从一个随机访问的巨大二进制文件中读取块。我有一个可能有几千个条目的偏移量和长度列表。用户选择一个条目,程序会寻找偏移量并读取长度字节。

程序内部使用 TMemoryStream 来存储和处理从文件中读取的块。读取数据是通过这样的 TFileStream 完成的:

FileStream.Position := Offset;
MemoryStream.CopyFrom(FileStream, Size);

这工作正常,但不幸的是,随着文件变大,它变得越来越慢。文件大小从几兆字节开始,但经常达到几十千兆字节。读取的块大小约为 100 KB。

文件的内容只能被我的程序读取。它是当时唯一访问该文件的程序。此外,文件存储在本地,因此这不是网络问题。

我在 Windows XP 机器上使用 Delphi 2007。

我可以做些什么来加快这个文件的访问速度?

编辑:

  • 无论读取文件的哪个部分,大文件的文件访问都很慢。
  • 程序通常不会顺序读取文件。块的顺序是用户驱动的,无法预测。
  • 从大文件中读取块总是比从小文件中读取同样大的块要慢。
  • 我说的是从文件中读取块的性能,而不是处理整个文件所需的总时间。对于较大的文件,后者显然需要更长的时间,但这不是问题所在。

我需要向大家道歉:在我按照建议使用内存映射文件实现文件访问之后,结果证明它并没有太大的区别。但是在我添加了更多的计时代码之后,结果也证明不是文件访问会减慢程序的速度。无论文件大小如何,文件访问实际上都需要几乎恒定的时间。用户界面的某些部分(我尚未确定)似乎在处理大量数据时存在性能问题,并且不知何故,当我第一次对流程进行计时时,我没有看到差异。

我很抱歉在确定瓶颈时马虎。

【问题讨论】:

  • 我没有看到任何明显的东西。这些流类只是系统文件 I/O 函数的包装器。您如何才能显着改进随机访问模式?
  • 您是说单次查找/读取对用户来说明显变慢了吗?或者这些操作中的大“批次”速度较慢?如果数据来自磁盘,则无论文件大小如何,单个查找和读取操作都应该大致相同。对于 7200 rpm 的磁盘,这应该在 5 到 10 毫秒之间。
  • 可能是内存碎片问题。您是否在操作之间释放 TMemoryStream?尝试在应用程序的整个生命周期内保持其活动状态,看看减速是否会消失。
  • @mark:这是从文件中读取块的单个操作,大文件比小文件慢得多。 (将此信息添加到问题中)
  • @Cosmin:它不会随着程序运行的时间变慢,但对于大文件来说启动很慢。

标签: delphi file-io delphi-2007 tfilestream


【解决方案1】:

如果您打开CreateFile() WinAPI 函数的帮助主题,您会在那里找到有趣的标志,例如 FILE_FLAG_NO_BUFFERING 和 FILE_FLAG_RANDOM_ACCESS 。你可以和他们一起玩以获得一些性能。

接下来,复制文件数据,即使是 100Kb 大小,也是一个额外的步骤,会减慢操作速度。使用CreateFileMapping 和 MapViewOfFile 函数来获取指向数据的准备使用指针是一个好主意。这样您就可以避免复制并且还可能获得某些性能优势(但您需要仔细测量速度)。

【讨论】:

  • 由于无论文件大小如何都会复制数据,因此不会成为瓶颈。
  • @dummzeuch 谁说的?你得到指向映射内存的指针。您不需要以这种方式复制并且可以直接访问映射的内存。 MMF 保存一个读数(至少)
  • 同意。 MMF 比使用普通文件 I/O 快得多。在我的一个项目中,我必须打开最大可达几 GB 的二进制日志文件。使用随机文件 I/O 查找此类文件可能需要几分钟,而使用 MMF 执行相同的工作只需很短的时间。
  • 嗯,所以你们都说使用内存映射文件总是比普通文件 I/O 快?如果这是真的,那么应该也可以将该内存复制到我的内存流中并且仍然具有速度优势,因此我不必进行太多更改来测试它。
  • @dummzeuch 让我再重复一遍 - 不要将内存复制到内存流!通过设置 TMemoryStream 对象的指针和大小属性,可以构造指向现有内存块的内存流。
【解决方案2】:

也许你可以采取这种方法:

对最大文件位置的条目进行排序,然后到以下内容:

  1. 获取只需要文件前 X MB 的条目(直到某个文件位置)
  2. 从文件中读取 X MB 到缓冲区 (TMemorystream
  3. 现在从缓冲区读取条目(可能是多线程的)
  4. 对所有条目重复此操作。

简而言之:缓存文件的一部分并读取适合它的所有条目(多线程),然后缓存下一部分等。

如果你只采用你原来的方法,也许你可以提高速度,但是按位置对条目进行排序。

【讨论】:

    【解决方案3】:

    Delphi 中的库存 TMemoryStream 由于其分配内存的方式而运行缓慢。 NexusDB 公司拥有效率更高的 TnxMemoryStream。可能有一些免费的,效果更好。

    现有的 Delphi TFileStream 也不是最有效的组件。历史上的回溯 Julian Bucknall 在杂志或其他可以非常有效地处理文件流的地方发布了一个名为 BufferedFileStream 的组件。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 2013-01-17
      • 2013-10-24
      • 2019-09-12
      • 2021-12-08
      • 2020-10-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多