【发布时间】:2010-11-18 01:47:04
【问题描述】:
我了解 Java 和 Perl 在读取文件时都非常努力地寻找一个适合所有默认缓冲区大小的大小,但我发现他们的选择越来越过时,并且在更改默认选择时遇到问题它涉及到 Perl。
在 Perl 的情况下,我相信它默认使用 8K 缓冲区,类似于 Java 的选择,我无法使用 perldoc 网站搜索引擎(实际上是 Google)找到有关如何增加默认文件输入的参考缓冲区大小说,64K。
从上面的链接,显示 8K 缓冲区如何不缩放:
如果每行通常有大约 60 个字符,那么 10,000 行文件中大约有 610,000 个字符。使用缓冲逐行读取文件只需要 75 次系统调用和 75 次等待磁盘,而不是 10,001 次。
因此,对于每行 60 个字符(包括末尾的换行符)的 50,000,000 行文件,以及 8K 缓冲区,它将进行 366211 次系统调用来读取 2.8GiB 文件。顺便说一句,您可以通过查看任务管理器进程列表中的磁盘 i/o 读取增量(至少在 Windows 中,*nix 中的顶部以某种方式显示相同的东西)来确认此行为作为您的 Perl 程序读取文本文件需要 10 分钟 :)
有人问关于在 perlmonks 上增加 Perl 输入缓冲区大小的问题,有人回答 here 你可以增加“$/”的大小,从而增加缓冲区大小,但是来自 perldoc:
将 $/ 设置为对整数的引用、包含整数的标量或可转换为整数的标量将尝试读取记录而不是行,最大记录大小为引用的整数。
所以我假设这实际上并没有增加 Perl 在使用典型时用于从磁盘读取的缓冲区大小:
while(<>) {
#do something with $_ here
...
}
“逐行”成语。
现在可能是上述代码的不同“一次读取记录然后将其解析为行”版本通常会更快,并且绕过标准习语的潜在问题并且无法更改默认缓冲区大小(如果这确实不可能),因为您可以将“记录大小”设置为您想要的任何内容,然后将每条记录解析为单独的行,希望 Perl 做正确的事情并结束每条记录执行一次系统调用,但这增加了复杂性,我真正想做的就是通过将上面示例中使用的缓冲区增加到相当大的大小(比如 64K)甚至调整缓冲区大小来轻松获得性能提升使用我的系统上的测试脚本来优化长读取的大小,无需额外的麻烦。
就增加缓冲区大小的直接支持而言,Java 中的情况要好得多。
在 Java 中,我相信 java.io.BufferedReader 使用的当前默认缓冲区大小也是 8192 字节,尽管 JDK 文档中的最新引用是模棱两可的,例如,1.5 文档只说:
可以指定缓冲区大小,也可以接受默认大小。对于大多数用途,默认值足够大。
幸运的是,使用 Java,您不必相信 JDK 开发人员已经为您的应用程序做出了正确的决定,并且可以设置自己的缓冲区大小(本例中为 64K):
import java.io.BufferedReader;
[...]
reader = new BufferedReader(new InputStreamReader(fileInputStream, "UTF-8"), 65536);
[...]
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
/* do something with the line here */
foo(line);
}
即使使用巨大的缓冲区和现代硬件,一次解析一行也只能挤出这么多的性能,而且我确信有办法通过读取文件来获得每一盎司的性能通过读取大的多行记录并将每个记录分解为标记,然后在每个记录中使用这些标记处理一次,但它们增加了复杂性和边缘情况(尽管如果在纯 Java 中有一个优雅的解决方案(仅使用 JDK 1.5 中存在的功能)很高兴知道)。增加 Perl 中的缓冲区大小至少可以解决 Perl 80% 的性能问题,同时让事情保持直截了当。
我的问题是:
有没有办法在 Perl 中为上述典型的“逐行”习语调整缓冲区大小,类似于 Java 示例中缓冲区大小的增加方式?
【问题讨论】:
标签: java perl file-io performance