Executando trabalhos em paralelo no Ubuntu - Diferenças de contenção de I / O entre Perl e Java

3

Desculpas se isso estiver fora do tópico - diz respeito à eficiência relativa da execução de scripts Perl / Java pesados em I / O em paralelo em um sistema Ubuntu.

Eu escrevi duas versões simples de um script de cópia de arquivo (Perl e Java) - veja abaixo. Quando eu executo os scripts em um arquivo de 15GB, cada um leva uma quantidade similar de tempo em uma máquina de 48 núcleos que executa o Ubuntu Server 12.04 (perl 2m10s, java 2m27s).

No entanto, quando executo seis instâncias em paralelo, cada uma operando em um arquivo de entrada diferente de 15 GB, observo tempos de processamento muito diferentes:

  • Perl: uma instância é concluída em 2m6s, todas as outras demoram 27m26s - 28m10s.
  • Java: todas as instâncias levam 3m27s - 4m37s.

Observando os núcleos do processador em top durante os processos Perl de longa execução, vejo que os núcleos ocupados têm porcentagens de espera de E / S (% wa) de 70% +, o que implica algum tipo de contenção de disco (todos os arquivos estão em um HD). Presumivelmente, então, o BufferedReader do Java é de alguma forma menos sensível a essa contenção de disco.

Pergunta - Isso parece uma conclusão razoável? E se assim for, alguém pode sugerir qualquer ação que eu possa tomar no nível do sistema operacional ou em Perl para tornar o script Perl tão eficiente quanto o Java para esse tipo de tarefa?

Observação - meu objetivo não é simplesmente copiar arquivos - meus scripts reais contêm lógica adicional, mas exibem o mesmo comportamento de desempenho que os scripts simplificados abaixo.

Perl

#!/usr/bin/perl -w
open(IN, $ARGV[0]) || die();
open(OUT, ">$ARGV[1]") || die();
while (<IN>) {
    print OUT $_
}
close(OUT);
close(IN);

Java

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();
        }
    }
}
    
por bashful 02.07.2012 / 21:43

3 respostas

2

A diferença de desempenho é mais provável em como funciona o buffer entre Perl e Java. Neste caso, você usou A bufferedReader em java, o que lhe dá uma vantagem. Perl faz buffer em torno de 4k do disco.

Você pode tentar algumas coisas aqui. Uma delas é usar a função read em perl para obter blocos maiores por vez. Isso pode melhorar o desempenho.

Outra opção pode ser investigar os vários módulos perl relacionados ao mmap.

    
por 02.07.2012 / 22:04
0

Não é realmente uma resposta, mas o código não formata bem nos comentários.

Para o GNU Parallel, eu uso uma versão dele para copiar. Pode entregar na ordem de 1 GB / s / core e funciona bem em paralelo:

perl -e '$left=-s STDIN;
  while($read=sysread(STDIN,$buf,$left>131072?131072:$left)){
    $left-=$read;
    syswrite(STDOUT,$buf);
  }' < in > out
    
por 23.04.2018 / 18:49
-2

Olá Isso pode não ser o caso, mas a partir da primeira observação, seu script perl está sendo executado de forma sequencial e intrepretada. Enquanto seu programa java está sendo executado como um programa compilado e está fazendo isso de forma paralela. Isso pode explicar a velocidade da diferença de conclusão.

    
por 02.07.2012 / 21:53