如果這不是主題,我們深表歉意 - 它涉及在 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 程式作為編譯程式運行並且以並行方式運行時。這可能是完成速度差異的原因。