Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

363
Views
¿Por qué la lectura de líneas desde stdin es mucho más lenta en C++ que en Python?

Quería comparar las líneas de lectura de la entrada de cadena desde stdin usando Python y C++ y me sorprendió ver que mi código C++ se ejecutaba un orden de magnitud más lento que el código Python equivalente. Como mi C++ está oxidado y todavía no soy un Pythonista experto, por favor díganme si estoy haciendo algo mal o si estoy malinterpretando algo.


( Respuesta de TLDR: incluya la declaración: cin.sync_with_stdio(false) o simplemente use fgets en su lugar.

Resultados de TLDR: desplácese hasta el final de mi pregunta y mire la tabla).


Código C++:

 #include <iostream> #include <time.h> using namespace std; int main() { string input_line; long line_count = 0; time_t start = time(NULL); int sec; int lps; while (cin) { getline(cin, input_line); if (!cin.eof()) line_count++; }; sec = (int) time(NULL) - start; cerr << "Read " << line_count << " lines in " << sec << " seconds."; if (sec > 0) { lps = line_count / sec; cerr << " LPS: " << lps << endl; } else cerr << endl; return 0; } // Compiled with: // g++ -O3 -o readline_test_cpp foo.cpp

Equivalente de Python:

 #!/usr/bin/env python import time import sys count = 0 start = time.time() for line in sys.stdin: count += 1 delta_sec = int(time.time() - start_time) if delta_sec >= 0: lines_per_sec = int(round(count/delta_sec)) print("Read {0} lines in {1} seconds. LPS: {2}".format(count, delta_sec, lines_per_sec))

Aquí están mis resultados:

 $ cat test_lines | ./readline_test_cpp Read 5570000 lines in 9 seconds. LPS: 618889 $ cat test_lines | ./readline_test.py Read 5570000 lines in 1 seconds. LPS: 5570000

Debo señalar que probé esto tanto en Mac OS X v10.6.8 (Snow Leopard) como en Linux 2.6.32 (Red Hat Linux 6.2). El primero es un MacBook Pro, y el segundo es un servidor muy robusto, no es que esto sea demasiado pertinente.

 $ for i in {1..5}; do echo "Test run $i at `date`"; echo -n "CPP:"; cat test_lines | ./readline_test_cpp ; echo -n "Python:"; cat test_lines | ./readline_test.py ; done
 Test run 1 at Mon Feb 20 21:29:28 EST 2012 CPP: Read 5570001 lines in 9 seconds. LPS: 618889 Python:Read 5570000 lines in 1 seconds. LPS: 5570000 Test run 2 at Mon Feb 20 21:29:39 EST 2012 CPP: Read 5570001 lines in 9 seconds. LPS: 618889 Python:Read 5570000 lines in 1 seconds. LPS: 5570000 Test run 3 at Mon Feb 20 21:29:50 EST 2012 CPP: Read 5570001 lines in 9 seconds. LPS: 618889 Python:Read 5570000 lines in 1 seconds. LPS: 5570000 Test run 4 at Mon Feb 20 21:30:01 EST 2012 CPP: Read 5570001 lines in 9 seconds. LPS: 618889 Python:Read 5570000 lines in 1 seconds. LPS: 5570000 Test run 5 at Mon Feb 20 21:30:11 EST 2012 CPP: Read 5570001 lines in 10 seconds. LPS: 557000 Python:Read 5570000 lines in 1 seconds. LPS: 5570000

Anexo y resumen de la pequeña evaluación comparativa

Para completar, pensé en actualizar la velocidad de lectura para el mismo archivo en el mismo cuadro con el código C++ original (sincronizado). Nuevamente, esto es para un archivo de 100 millones de líneas en un disco rápido. Aquí está la comparación, con varias soluciones/enfoques:

Implementación lineas por segundo
pitón (predeterminado) 3.571.428
cin (predeterminado/ingenuo) 819,672
cin (sin sincronización) 12,500,000
fgets 14,285,714
wc (comparación no justa) 54.644.808
over 4 years ago · Santiago Trujillo
9 answers
Answer question

0

Un primer elemento de una respuesta: <iostream> es lento. Maldita sea lento. Obtengo un gran aumento de rendimiento con scanf como se muestra a continuación, pero aún es dos veces más lento que Python.

 #include <iostream> #include <time.h> #include <cstdio> using namespace std; int main() { char buffer[10000]; long line_count = 0; time_t start = time(NULL); int sec; int lps; int read = 1; while(read > 0) { read = scanf("%s", buffer); line_count++; }; sec = (int) time(NULL) - start; line_count--; cerr << "Saw " << line_count << " lines in " << sec << " seconds." ; if (sec > 0) { lps = line_count / sec; cerr << " Crunch speed: " << lps << endl; } else cerr << endl; return 0; }
over 4 years ago · Santiago Trujillo Report

0

tl; dr: debido a las diferentes configuraciones predeterminadas en C ++ que requieren más llamadas al sistema.

De forma predeterminada, cin está sincronizado con stdio, lo que hace que evite cualquier almacenamiento en búfer de entrada. Si agrega esto a la parte superior de su principal, debería ver un rendimiento mucho mejor:

 std::ios_base::sync_with_stdio(false);

Normalmente, cuando se almacena en búfer un flujo de entrada, en lugar de leer un carácter a la vez, el flujo se leerá en fragmentos más grandes. Esto reduce el número de llamadas al sistema, que suelen ser relativamente caras. Sin embargo, dado que los stdio e iostreams basados en FILE* a menudo tienen implementaciones separadas y, por lo tanto, búferes separados, esto podría generar un problema si ambos se usaran juntos. Por ejemplo:

 int myvalue1; cin >> myvalue1; int myvalue2; scanf("%d",&myvalue2);

Si cin leyó más entradas de las que realmente necesitaba, entonces el segundo valor entero no estaría disponible para la función scanf , que tiene su propio búfer independiente. Esto conduciría a resultados inesperados.

Para evitar esto, de forma predeterminada, las transmisiones se sincronizan con stdio . Una forma común de lograr esto es hacer que cin lea cada carácter uno a la vez según sea necesario usando las funciones de stdio . Desafortunadamente, esto introduce muchos gastos generales. Para pequeñas cantidades de entrada, esto no es un gran problema, pero cuando lee millones de líneas, la penalización del rendimiento es significativa.

Afortunadamente, los diseñadores de la biblioteca decidieron que también debería poder deshabilitar esta función para obtener un mejor rendimiento si sabía lo que estaba haciendo, por lo que proporcionaron el método sync_with_stdio . Desde este enlace (énfasis añadido):

Si la sincronización está desactivada, los flujos estándar de C++ pueden almacenar en búfer su E/S de forma independiente, lo que puede ser considerablemente más rápido en algunos casos .

over 4 years ago · Santiago Trujillo Report

0

En su segundo ejemplo (con scanf() ), la razón por la que esto es aún más lento podría deberse a que scanf("%s") analiza la cadena y busca cualquier carácter de espacio (espacio, tabulador, nueva línea).

Además, sí, CPython hace algo de almacenamiento en caché para evitar lecturas del disco duro.

over 4 years ago · Santiago Trujillo Report

0

Reproduje el resultado original en mi computadora usando g++ en una Mac.

Agregar las siguientes declaraciones a la versión de C++ justo antes del ciclo while lo pone en línea con la versión de Python :

 std::ios_base::sync_with_stdio(false); char buffer[1048576]; std::cin.rdbuf()->pubsetbuf(buffer, sizeof(buffer));

sync_with_stdio mejoró la velocidad a 2 segundos, y la configuración de un búfer más grande la redujo a 1 segundo.

over 4 years ago · Santiago Trujillo Report

0

Bueno, veo que en tu segunda solución cambiaste de cin a scanf , que era la primera sugerencia que te iba a hacer ( cin es lentooooooooooo). Ahora, si cambia de scanf a fgets , verá otro aumento en el rendimiento: fgets es la función de C++ más rápida para la entrada de cadenas.

Por cierto, no sabía nada de eso de la sincronización, genial. Pero aún deberías probar fgets .

over 4 years ago · Santiago Trujillo Report

0

Por cierto, la razón por la que el recuento de líneas para la versión de C++ es uno mayor que el recuento de la versión de Python es que el indicador eof solo se establece cuando se intenta leer más allá de eof. Así que el bucle correcto sería:

 while (cin) { getline(cin, input_line); if (!cin.eof()) line_count++; };
over 4 years ago · Santiago Trujillo Report

0

Solo por curiosidad, eché un vistazo a lo que sucede debajo del capó y usé dtruss/strace en cada prueba.

C++

 ./a.out < in Saw 6512403 lines in 8 seconds. Crunch speed: 814050

syscalls sudo dtruss -c ./a.out < in

 CALL COUNT __mac_syscall 1 <snip> open 6 pread 8 mprotect 17 mmap 22 stat64 30 read_nocancel 25958

Pitón

 ./a.py < in Read 6512402 lines in 1 seconds. LPS: 6512402

syscalls sudo dtruss -c ./a.py < in

 CALL COUNT __mac_syscall 1 <snip> open 5 pread 8 mprotect 17 mmap 21 stat64 29
over 4 years ago · Santiago Trujillo Report

0

getline , operadores de flujo, scanf , pueden ser convenientes si no le importa el tiempo de carga del archivo o si está cargando archivos de texto pequeños. Pero, si el rendimiento es algo que le importa, realmente debería almacenar todo el archivo en memoria intermedia (suponiendo que quepa).

Aquí hay un ejemplo:

 //open file in binary mode std::fstream file( filename, std::ios::in|::std::ios::binary ); if( !file ) return NULL; //read the size... file.seekg(0, std::ios::end); size_t length = (size_t)file.tellg(); file.seekg(0, std::ios::beg); //read into memory buffer, then close it. char *filebuf = new char[length+1]; file.read(filebuf, length); filebuf[length] = '\0'; //make it null-terminated file.close();

Si lo desea, puede envolver un flujo alrededor de ese búfer para un acceso más conveniente como este:

 std::istrstream header(&filebuf[0], length);

Además, si tiene el control del archivo, considere usar un formato de datos binario plano en lugar de texto. Es más confiable leer y escribir porque no tiene que lidiar con todas las ambigüedades de los espacios en blanco. También es más pequeño y mucho más rápido de analizar.

over 4 years ago · Santiago Trujillo Report

0

El siguiente código fue más rápido para mí que el otro código publicado aquí hasta ahora: (Visual Studio 2013, 64 bits, archivo de 500 MB con longitud de línea uniforme en [0, 1000)).

 const int buffer_size = 500 * 1024; // Too large/small buffer is not good. std::vector<char> buffer(buffer_size); int size; while ((size = fread(buffer.data(), sizeof(char), buffer_size, stdin)) > 0) { line_count += count_if(buffer.begin(), buffer.begin() + size, [](char ch) { return ch == '\n'; }); }

Supera todos mis intentos de Python por más de un factor 2.

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!