¿Por qué Java lee un archivo grande más rápido que C++?


Tengo un archivo de 2 GB (iputfile.txt) en el que cada línea en el archivo es una palabra, al igual que:

apple
red
beautiful
smell
spark
input

Necesito escribir un programa para leer cada palabra en el archivo e imprimir el recuento de palabras. Lo escribí usando Java y C++, pero el resultado es sorprendente: Java se ejecuta 2,3 veces más rápido que C++. Mi código es el siguiente:

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.10f s\n", cost);

    return 0;
}

Salida:

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");
}

Salida:

5.0E8
Run time: 29 s

¿Por qué Java es más rápido que C++ en esta situación, y cómo mejorar la rendimiento de C++?

Author: Peter Mortensen, 2014-04-09

5 answers

No estás comparando lo mismo. El programa Java lee líneas, dependiendo de la nueva línea, mientras que el programa C++ lee "palabras" delimitadas por espacios en blanco, lo cual es un poco más de trabajo.

Intenta istream::getline.

Más Tarde

También puede intentar hacer una operación de lectura elemental para leer una matriz de bytes y buscar nuevas líneas.

Incluso después

En mi viejo portátil Linux, jdk1. 7. 0_21 y don't-tell-me-it's-old 4.3.3 toman aproximadamente el mismo tiempo, comparando con C++ getline. (Hemos establecido que leer palabras es más lento.) No hay mucha diferencia entre-O0 y-O2, lo que no me sorprende, dada la simplicidad del código en el bucle.

Última nota Como sugerí, Fin.read (buffer, LEN) con LEN = 1MB y el uso de memchr para buscar '\n' resulta en otra mejora de la velocidad de alrededor del 20%, lo que hace que C (ya no queda C++) sea más rápido que Java.

 64
Author: laune,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 17:10:21

Hay una serie de diferencias significativas en la forma en que los lenguajes manejan E / S , todo lo cual puede hacer una diferencia, de una manera u otro.

Tal vez la primera (y más importante) pregunta es: ¿cómo es la datos codificados en el archivo de texto. Si se trata de caracteres de un solo byte (ISO 8859-1 o UTF-8), entonces Java tiene que convertirlo en UTF-16 antes de procesar; dependiendo de la configuración regional, C++ puede (o no) también convertir o hacer algunos adicionales comprobación.

Como se ha señalado (parcialmente, al menos), en C++, >> utiliza un local específico isspace, getline simplemente comparar para '\n', que es probablemente más rápido. (Implementaciones típicas de isspace utilizará un mapa de bits, lo que significa una memoria adicional acceso para cada personaje.)

Los niveles de optimización y las implementaciones de bibliotecas específicas pueden también varían. No es inusual en C++ para una biblioteca la implementación debe ser 2 o 3 veces más rápida que otra.

Finalmente, un diferencia más significativa: C++ distingue entre archivos de texto y archivos binarios. Has abierto el archivo en modo texto; esto significa que será "preprocesado" en el nivel más bajo, incluso antes de que los operadores de extracción lo vean. Este depende de la plataforma: para las plataformas Unix, el " preprocesamiento" es un no-op; en Windows, convertirá los pares CRLF en '\n', lo que tendrá un impacto definitivo en el rendimiento. Si recuerdo correctamente (no he usado Java durante algunos años), Java espera nivel superior funciones para manejar esto, por lo que funciones como readLine será un poco más complicado. Sólo adivinando. aquí, pero sospecho que la lógica adicional en el más alto nivel cuesta menos en tiempo de ejecución que el preprocesamiento de búfer en el nivel inferior. (Si está probando bajo Windows, es posible que experimente con la apertura del archivo en modo binario en C++. Este no debe hacer ninguna diferencia en el comportamiento del programa cuando utilice >>; cualquier CR adicional se considerará espacio en blanco. Con getline, tendrás que añadir lógica para eliminar cualquier final '\r' a su código.)

 7
Author: James Kanze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 17:05:32

Sospecharía que la principal diferencia es que java.io.BufferedReader funciona mejor que std::ifstream porque almacena en búfer, mientras que el ifsteam no lo hace. El BufferedReader lee grandes trozos del archivo por adelantado y los entrega a su programa desde la RAM cuando llama readLine(), mientras que el std::ifstream solo lee unos pocos bytes a la vez cuando se le pide que llame al operador >>.

El acceso secuencial de grandes cantidades de datos desde el disco duro suele ser mucho más rápido que el acceso a muchos pequeños trozos uno a la vez.

Una comparación más justa sería comparar std:: ifstream con el java.io.FileReader sin búfer.

 5
Author: Philipp,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 08:45:16

No soy experto en C++, pero tiene al menos lo siguiente para afectar el rendimiento:

  1. Almacenamiento en caché a nivel del sistema operativo para el archivo
  2. Para Java está utilizando un lector de búfer y el tamaño predeterminado del búfer es una página o algo así. No estoy seguro de cómo C++ streams hace esto.
  3. Dado que el archivo es tan grande que JIT probablemente sería pateado, y probablemente compila el código de bytes de Java mejor que si no activa ninguna optimización para su compilador de C++.

Desde I/O el costo es el costo principal aquí, supongo que 1 y 2 son las razones principales.

 4
Author: Alex Suo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 17:03:00

También intentaría usar mmap en lugar de lectura/escritura de archivos estándar. Esto debería permitir que su sistema operativo maneje la lectura y escritura mientras que su aplicación solo se ocupa de los datos.

No hay ninguna situación en la que C++ no pueda ser más rápido que Java, pero a veces requiere mucho trabajo de personas muy talentosas. Pero no creo que este deba ser demasiado difícil de superar, ya que es una tarea sencilla.

Mmap para Windows se describe en Mapeo de archivos ( MSDN ).

 2
Author: rich remer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-09 17:10:27