如果这不是主题,我们深表歉意 - 它涉及在 Ubuntu 系统上并行运行 I/O 密集型 Perl/Java 脚本的相对效率。
我编写了两个简单版本的文件复制脚本(Perl 和 Java) - 见下文。当我在 15GB 文件上运行脚本时,每个脚本在运行 Ubuntu Server 12.04 的 48 核计算机上花费的时间相似(perl 2 分 10 秒,java 2 分 27 秒)。
但是,当我并行运行 6 个实例(每个实例运行不同的 15GB 输入文件)时,我观察到处理时间差异很大:
- Perl:一个实例在 2 分 6 秒内完成,所有其他实例则需要 27 分 26 秒 - 28 分 10 秒。
- Java:所有实例需要 3 分 27 秒 - 4 分 37 秒。
查看top
长时间运行的 Perl 进程期间的处理器核心,我发现占用的核心的 I/O 等待百分比 (%wa) 超过 70%,这意味着存在某种磁盘争用(所有文件都在一个 HD 上)。那么,大概 JavaBufferedReader
对这种磁盘争用不太敏感。
问题 - 这看起来是一个合理的结论吗?如果是这样,有人可以建议我可以在操作系统级别或 Perl 中采取任何操作,以使 Perl 脚本与 Java 一样高效地完成此类任务吗?
注意 - 我的目标不仅仅是复制文件 - 我的真实脚本包含额外的逻辑,但表现出与下面的简化脚本相同的性能行为。
珀尔
#!/usr/bin/perl -w
open(IN, $ARGV[0]) || die();
open(OUT, ">$ARGV[1]") || die();
while (<IN>) {
print OUT $_
}
close(OUT);
close(IN);
爪哇
import java.io.*;
public class CopyFileLineByLine {
public static void main(String[] args) throws IOException {
BufferedReader br = null;
PrintWriter pw = null;
try {
br = new BufferedReader(new FileReader(new File(args[0])));
pw = new PrintWriter(new File(args[1]));
String line;
while ((line = br.readLine()) != null) {
pw.println(line);
}
}
finally {
if (pw != null) pw.close();
if (br != null) br.close();
}
}
}
答案1
性能差异很可能在于 Perl 和 Java 之间的缓冲工作方式。在本例中,您使用了 java 中的 bufferedReader,这给了它一个优势。 Perl 确实从磁盘缓冲了大约 4k。
你可以在这里尝试一些事情。一种是使用perl中的read函数一次获取更大的块。那可能提高性能。
另一种选择可能是研究各种与 mmap 相关的 perl 模块。
答案2
不是真正的答案,但代码在注释中的格式不正确。
对于 GNU Parallel,我使用它的一个版本来复制。它可以按 1 GB/s/core 的顺序交付,并且并行运行良好:
perl -e '$left=-s STDIN;
while($read=sysread(STDIN,$buf,$left>131072?131072:$left)){
$left-=$read;
syswrite(STDOUT,$buf);
}' < in > out
答案3
您好 情况可能并非如此,但从初步观察来看,您的 perl 脚本以顺序解释方式运行。而您的 java 程序则作为编译程序运行,并以并行方式运行。这可能是完成速度差异的原因。