Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

368
Visualizações
¿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 Respostas
Responde à pergunta

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 Relatório

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 Relatório

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 Relatório

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 Relatório

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 Relatório

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 Relatório

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 Relatório

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda