【问题标题】:What is the fastest way to 'print' to file in perl?在 perl 中“打印”文件的最快方法是什么?
【发布时间】:2012-03-27 20:13:09
【问题描述】:

一段时间以来,我一直在使用以下代码将 perl 脚本的输出写入文件:

open( OUTPUT, ">:utf8", $output_file ) or die "Can't write new file: $!";

print OUTPUT "First line I want printed\n";
print OUTPUT "Another line I want printing\n";

close(OUTPUT);

这很有效,并且比我最初使用“say”而不是 print 的方法更快(感谢 NYTProf 启发我!)

但是,我当前的脚本循环了数十万行,并且使用此方法需要花费数小时才能运行,而 NYTProf 将矛头指向我的数千个“打印”命令。所以,问题是... 有更快的方法吗?

其他可能相关的信息... Perl 版本:5.14.2(在 Ubuntu 上)

相关脚本的背景... 多个'|'分隔的平面文件被读入散列,每个文件都有某种从一个到另一个的主键匹配条目。我正在处理这些数据,他们将它们组合到一个文件中以导入另一个系统。

输出文件大约有 300 万行,在向该文件写入大约 30,000 行后,程序开始明显变慢。 (一点点阅读似乎指向用完其他语言的写入缓冲区,但我找不到关于 perl 的任何信息?)

编辑:我现在尝试在 open() 语句之后添加以下行,以禁用打印缓冲,但程序在第 30,000 行左右仍然变慢。

OUTPUT->autoflush(1);

【问题讨论】:

  • autoflush 会减慢您的输出速度,而不是加快输出速度。它强制在每个 print 语句中将数据写入磁盘,而不是等待 IO 缓冲区填满。是什么让您认为问题在于输出的速度?我认为这更有可能是在准备数据时所做的工作。我建议您尝试禁用打印语句,看看您的程序在没有输出的情况下如何执行。
  • 试试 syswrite(OUTPUT, "..."); .它比 STDOUT 的打印语句更快。
  • @Nick: syswrite 本身,没有任何用户编码的缓冲,将具有与autoflush 类似的效果,并强制对每条记录进行磁盘写入。编写一个自制的输出缓冲系统与syswrite 一起使用似乎不太可能加快速度,但我想这是可能的。
  • @Ashimema 您几乎可以肯定专注于错误的事情。考虑您的算法、数据结构和内存使用情况。缓慢可能存在于那里,而不是 Perl 高效写入文件的能力。
  • 这真的是 Unicode 数据,还是只是 ASCII?

标签: perl buffer


【解决方案1】:

我认为您需要重新设计程序使用的算法。文件输出速度不受已输出数据量的影响,更有可能是您的程序正在读取和处理数据而不是释放数据。

  • 检查进程使用的内存量,看看它是否会无情地增加

  • 注意for (<$filehandle>) 循环,它会一次将整个文件读入内存

  • 正如我在评论中所说,禁用相关的print 语句以查看性能如何变化

【讨论】:

  • 这是个好建议。 Perl 不应该关心您是在输出的第 1 行还是第 100,000,000 行。一段时间后的显着减速听起来很像淹没 RAM 并进入虚拟 RAM(即交换到硬盘驱动器)。寻找未被销毁的对象、循环引用、变量累积越来越长的字符串等。
  • 嗯,进程的内存使用量似乎并没有无情地增加,所以我不认为这是问题所在。我正在使用 Text::CSV::Encoded 模块将文件解析为哈希,但这似乎是另一个瓶颈。至于禁用打印语句,您禁用它们的权利对程序的整体运行时间影响不大,因此尽管 NYTProf 声称“打印”是最长的操作之一,但我认为我找错了地方。
  • @Ashimema:磁盘输出的时间总是比大多数处理时间长数千倍。删除输出语句后,分析器会说什么?你想展示你的程序以便我看一下吗?如果您愿意,我们可以在清单外进行。
  • @Borodin:几年前我最终对脚本进行了大量重构,并且在我的 perl 编程方面有了很大的改进。但是感谢这么多年后的提议;)
【解决方案2】:

您是否尝试过将所有单个打印内容合并为单个标量,然后一次打印所有标量?我有一个脚本,每个输入行平均输出 20 行文本。使用单独的打印语句时,即使将输出发送到 /dev/null,也需要很长时间。但是当我将所有输出(对于单个输入行)打包在一起时,使用以下内容:

$output .= "...";

$output .= sprintf("%s...", $var);

然后就在离开生产线处理子程序之前,我“打印$output”。一次打印所有行。调用打印的次数从 ~7.7M 到大约 386K - 等于输入日期文件中的行数。这将我的总执行时间减少了大约 10%。

【讨论】:

  • 作为非 Perl 注释,如果您将 $output 替换为第一个变量,它可能会再次加速(而不是 .= )它在 Java 和 JS 中是这样;由于模板,我还没有在 Perl 中这样做。
猜你喜欢
  • 2021-11-08
  • 1970-01-01
  • 1970-01-01
  • 2013-03-15
  • 2010-09-24
  • 2016-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多