在 Ubuntu 上并行运行作业 - Perl 和 Java 之间的 I/O 争用差异

在 Ubuntu 上并行运行作业 - Perl 和 Java 之间的 I/O 争用差异

如果这不是主题,我们深表歉意 - 它涉及在 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 程序则作为编译程序运行,并以并行方式运行。这可能是完成速度差异的原因。

相关内容