Por que o Java lê um arquivo grande mais rápido que o C ++?

Eu tenho um arquivo de 2 GB ( iputfile.txt ) em que cada linha no arquivo é uma palavra, assim como:

 apple red beautiful smell spark input 

Eu preciso escrever um programa para ler cada palavra no arquivo e imprimir a contagem de palavras. Eu escrevi usando Java e C ++, mas o resultado é surpreendente: o Java roda 2,3 vezes mais rápido que o C ++. Meu código é o seguinte:

C ++:

 int main() { struct timespec ts, te; double cost; clock_gettime(CLOCK_REALTIME, &ts); ifstream fin("inputfile.txt"); string word; int count = 0; while(fin >> word) { count++; } cout << count << endl; clock_gettime(CLOCK_REALTIME, &te); cost = te.tv_sec - ts.tv_sec + (double)(te.tv_nsec-ts.tv_nsec)/NANO; printf("Run time: %-15.10fs\n", cost); return 0; } 

Saída:

 5e+08 Run time: 69.311 s 

Java:

  public static void main(String[] args) throws Exception { long startTime = System.currentTimeMillis(); FileReader reader = new FileReader("inputfile.txt"); BufferedReader br = new BufferedReader(reader); String str = null; int count = 0; while((str = br.readLine()) != null) { count++; } System.out.println(count); long endTime = System.currentTimeMillis(); System.out.println("Run time : " + (endTime - startTime)/1000 + "s"); } 

Saída:

 5.0E8 Run time: 29 s 

Por que o Java é mais rápido que o C ++ nessa situação e como eu melhoro o desempenho do C ++?

Você não está comparando a mesma coisa. O programa Java lê linhas, dependendo da nova linha, enquanto o programa C ++ lê “palavras” delimitadas por espaços em branco, o que é um pequeno trabalho extra.

Tente istream::getline .

Mais tarde

Você também pode tentar fazer uma operação de leitura elementar para ler uma matriz de bytes e verificar isso para novas linhas.

Até mais tarde

No meu antigo notebook Linux, o jdk1.7.0_21 e o 4.3.3 tomam o mesmo tempo, comparando com o C ++ getline. (Nós estabelecemos que ler palavras é mais lento.) Não há muita diferença entre -O0 e -O2, o que não me surpreende, dada a simplicidade do código no loop.

Última observação Como eu sugeri, fin.read (buffer, LEN) com LEN = 1MB e usando memchr para procurar por ‘\ n’ resulta em outra melhoria de velocidade de cerca de 20%, o que torna C (não há nenhum C ++ deixado por agora) mais rápido que o Java.

Há várias diferenças significativas na maneira como os idiomas lidam com E / S , e todos podem fazer a diferença, de uma forma ou de outra.

Talvez a primeira (e mais importante) pergunta seja: como os dados são codificados no arquivo de texto. Se forem caracteres de byte único ( ISO 8859-1 ou UTF-8 ), o Java precisará convertê-lo em UTF-16 antes do processamento; dependendo da localidade, o C ++ pode (ou não) também converter ou fazer alguma verificação adicional.

Como foi apontado (parcialmente, pelo menos), em C ++, >> usa um espaço de caractere específico de localidade, o getline simplesmente irá comparar para '\n' , que é provavelmente mais rápido. (Implementações típicas do isspace usarão um bitmap, o que significa um access de memory adicional para cada caractere.)

Níveis de otimização e implementações de bibliotecas específicas também podem variar. Não é incomum em C ++ a implementação de uma biblioteca ser 2 ou 3 vezes mais rápida do que outra.

Finalmente, uma diferença muito significativa: o C ++ distingue entre arquivos de texto e arquivos binários. Você abriu o arquivo no modo de texto; isso significa que ele será “pré-processado” no nível mais baixo, antes mesmo que os operadores de extração o vejam. Isso depende da plataforma: para plataformas Unix, o “pré-processamento” é um não operacional; no Windows, ele converterá pares CRLF em '\n' , o que terá um impacto definido no desempenho. Se bem me lembro (eu não usei o Java por alguns anos), o Java espera que funções de alto nível lidem com isso, então funções como o readLine serão um pouco mais complicadas. Apenas supondo aqui, mas suspeito que a lógica adicional no nível mais alto custa menos em tempo de execução do que o pré-processamento do buffer no nível inferior. (Se você está testando no Windows, você pode experimentar abrir o arquivo no modo binário em C ++. Isso não deve fazer diferença no comportamento do programa quando você usa >> ; qualquer CR extra será considerado espaço em branco. você terá que adicionar lógica para remover qualquer '\r' ao seu código.)

Eu suspeito que a principal diferença é que java.io.BufferedReader executa melhor que o std::ifstream porque ele armazena em buffer, enquanto o ifsteam não. O BufferedReader lê grandes partes do arquivo antecipadamente e as entrega ao seu programa da RAM quando você chama readLine() , enquanto o std :: ifstream apenas lê alguns bytes de cada vez quando você o solicita chamando o >> – operador.

O access sequencial de grandes quantidades de dados a partir do disco rígido é geralmente muito mais rápido do que o access a vários pequenos blocos, um de cada vez.

Uma comparação mais justa seria comparar std :: ifstream ao java.io.FileReader sem buffer.

Eu não sou especialista em C ++, mas você tem pelo menos o seguinte para afetar o desempenho:

  1. Armazenamento em cache no nível do sistema operacional para o arquivo
  2. Para Java, você está usando um leitor em buffer e o tamanho do buffer é padronizado para uma página ou algo assim. Não tenho certeza de como o C ++ faz isso.
  3. Já que o arquivo é tão grande que o JIT provavelmente seria kickado, e provavelmente compila o código de byte Java melhor do que se você não ativasse nenhuma otimização para seu compilador C ++.

Como o custo de I / O é o maior custo aqui, acho que 1 e 2 são os principais motivos.

Eu também tentaria usar o mmap em vez do arquivo padrão de leitura / gravação. Isso deve permitir que seu sistema operacional lide com a leitura e a gravação enquanto seu aplicativo está preocupado apenas com os dados.

Não há nenhuma situação em que o C ++ não possa ser mais rápido que o Java, mas às vezes é preciso muito trabalho de pessoas muito talentosas. Mas eu não acho que este deve ser muito difícil de bater, pois é uma tarefa simples.

O mmap para Windows é descrito no Mapeamento de Arquivos ( MSDN ).