【问题标题】:fastest way to read large text file读取大文本文件的最快方法
【发布时间】:2017-01-04 22:01:42
【问题描述】:

我希望从大型(~870,000,000 行/~4GB)文本文件中提取某些行组。举个小例子,在一个 50 行的文件中,我可能需要第 3-6、18-27 和 39-45 行。使用 SO 开始,并编写一些程序来对我的数据进行基准测试,似乎 fortran90 给了我最好的结果(与 python、shell 命令(bash)等相比)。

我当前的方案只是打开文件并使用一系列循环将读取指针移动到我需要的位置并将结果写入输出文件。

使用上面的小例子,它看起来像:

    open(unit=1,fileName)
    open(unit=2,outFile)

    do i=1,2
      read(1,*)
    end do
    do i=3,6
      read(1,*) line
      write(2,*) line
    end do
    do i=7,17
      read(1,*)
    end do
    do i=18,27
      read(1,*) line
      write(2,*) line
    end do
    do i=28,38
      read(1,*)
    end do
    do i=39,45
      read(1,*) line
      write(2,*) line
    end do

*应该注意的是,我在编译时假设缓冲 i/o,尽管这似乎只是最低限度地加快了速度。

我很好奇这是否是完成我的任务的最有效方式。如果上述方法实际上是使用 fortran90 执行此操作的最佳方法,是否还有其他语言更适合此任务?

*更新:确保我使用的是缓冲 i/o,手动查找最有效的块大小/块计数。这将速度提高了约 7%。请注意,我正在使用的文件没有固定的记录长度。

【问题讨论】:

  • 读取大文本文件的最快简便方法?把它放在一个非常快的磁盘系统上,不要写慢代码来读它,以免伤到自己的脚。还有更复杂的解决方案,但它们非常依赖于操作系统,并且实际的 IO 模式非常依赖于硬件,因为 最快 方式通常会完全绕过页面缓存。对于它的价值,mmap() 通常是一个非常糟糕的选择,当您只想从头到尾流式传输文件而不重新读取文件的任何部分时。 mmap() 在您必须从整个文件的随机位置进行多次重新读取时效果最佳。
  • 也许使用STREAM 访问会比按顺序跳过特定行更快。
  • 谢谢,我会看看使用流 i/o 是如何工作的。
  • 通过流访问,您需要解析文件以查找行尾标记,我看不出这有什么帮助..(除非所有行的长度完全相同。)跨度>
  • 是的,我得出了同样的结论,即流访问无济于事。不幸的是,这些条目的长度不相等,因此减少了一些简单的解决方案。最终坚持使用类似于我上面原来的方案。在 C++ 中有一个解决方案:stackoverflow.com/questions/26736742/…,看起来很有希望,但我宁愿给我的代码额外的时间,也不愿为这项任务学习 C++。

标签: file text fortran fortran90


【解决方案1】:

您也可以尝试使用sed 实用程序。

sed '3,6!d' yourfile.txt
sed '18,27!d' yourfile.txt

Unix 实用程序往往非常优化,并且可以非常快速地解决此类简单任务。

【讨论】:

  • 感谢您的回复!我确实尝试了一个 sed 解决方案(即 sed -n “3,6p;18,27p;39,45p;45q” 文件名),无论它的结构如何(awk 是相似的)。如果文件不是那么大,那就没那么重要了,但我正在寻找最快的解决方案,即使它有点复杂。
  • 是的,我明白你想要做什么,我也有一些类似的任务。无论如何,这些实用程序总是值得一试。此外,您可能会尝试在 C 中实现这个东西。准确读取 fs 的块大小,手动扫描换行,仔细(!)计算它们,并将缓冲区拼接在一起。并注意处理没有换行符的 10G 文件不会使您的机器崩溃。
  • 以防万一您没有想到 - 如果您可以访问生成这些大文件的代码,您可以在那里挖掘,也许在那里准备您需要的东西。
  • 另外,如果碰巧你的线条长度都相同,那么你非常幸运,你可以手动寻找你需要的线条,这肯定比其他任何东西都要快。但我猜它们的长度不同......
  • 无论如何,仔细看看您的解决方案,我认为如果您自己阅读更大的块和流程线,您当前的解决方案肯定会得到改进。
【解决方案2】:

几乎任何语言都应该能够做到这一点,所以如果你修正错别字,坚持这里的主题应该接近工作。 (如果我在 iPad 上有一个 fortran 编译器,它会更有用。)

PROGRAM AA
IMPLICIT NONE
INTEGER :: In_Unit, Out_Unit, I
LOGICAL, DIMENSION(1000) :: doIt
CHARACTER(LEN=20) :: FileName = 'in.txt'
CHARACTER(LEN=20) :: Outfile = 'out.txt'
CHARACTER(LEN=80) :: line

open(NEWunit=In_Unit,  fileName)  ! Status or action = read only??
open(NEWunit=Out_Unit, outFile)   ! Status or action = new or readwrite??

DoIt        = .FALSE.
DoIt(3:6)   = .TRUE.
DoIt(18:27) = .TRUE.
DoIt(39:45) = .TRUE.

do i=1,1000
  read(I_Unit,*) line
  IF(doIt(I)) write(Out_Unit,*) line
end do

CLOSE(In_Unit)
CLOSE(Out_Unit)

END PROGRAM AA

【讨论】:

  • 这绝对不会比 OP 提出的更快,而这正是他所问的。首先,您在循环中嵌入了一个条件,并且您还阅读并解析每一行,而 OP 的代码仅读取和解析他感兴趣的行。对我投反对票。
  • @Ross 我想你说得有道理。在 4GB 文件前面几行的例子中,顺序读取应该没有区别。在我的带有一些 RAID 的工作机器上,一个 4GB 的文件在大约 15 秒内被缓冲。但这是使用对 line() 数组的读取,然后在循环中执行琐碎的条件。确实需要将读取性能与条件分开查看。因此,我认为您可能正在“了解”(或暗示)将非顺序和非流式输入作为“最快”的输入?但帖子似乎也要求基本 IO。
猜你喜欢
  • 2012-08-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-03
相关资源
最近更新 更多