¿Usar scanf () en programas C ++ es más rápido que usar cin?

126

No sé si esto es cierto, pero cuando estaba leyendo las preguntas frecuentes sobre uno de los sitios de problemas, encontré algo que me llamó la atención:

Verifique sus métodos de entrada / salida. En C ++, usar cin y cout es demasiado lento. Úselos y garantizará que no podrá resolver ningún problema con una cantidad decente de entrada o salida. Utilice printf y scanf en su lugar.

¿Alguien puede aclarar esto? ¿Realmente está usando scanf () en programas C ++ más rápido que usando cin >> algo ? En caso afirmativo, ¿es una buena práctica usarlo en programas C ++? Pensé que era específico de C, aunque solo estoy aprendiendo C ++ ...

zeroDivisible
fuente
14
Mi suposición: un mal programador culpa a las bibliotecas estándar por su bajo rendimiento. Algo así como el siempre chistoso grito "Creo que encontré un error en el CCG".
John Kugelman
11
@eclipse: los problemas de ACM en los que he trabajado para competiciones tienen una cantidad sustancial de entrada / salida y su programa tiene que resolver las preguntas en menos de 60 segundos ... se convierte en un problema real aquí.
mpen
19
--- dijo que, si tiene que depender de scanf () para ese rendimiento extra, que va sobre el problema de manera equivocada :)
MPes
44
Solo como una observación: jugué con él y en el segundo problema (PRIME1), usando el mismo algoritmo, ambas veces, una vez usando cin / cout y una vez con scanf / printf y la primera versión fue más rápida que la segunda (pero lo suficientemente cerca como para que sea estadísticamente irrelevante). Este es uno de los problemas que se marca como intensivo de entrada / salida, y el método de entrada / salida no hizo ninguna diferencia estadística.
Eclipse
44
@Eclipse: gracias por la información sobre la prueba de ambos métodos. Sin embargo, estoy triste: traté de culpar a cin and cout, pero ahora sé que mi algoritmo apesta :)
zeroDivisible

Respuestas:

209

Aquí hay una prueba rápida de un caso simple: un programa para leer una lista de números de entrada estándar y XOR todos los números.

versión iostream:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

versión scanf:

#include <stdio.h>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  while (1 == scanf("%d", &x))
    parity ^= x;
  printf("%d\n", parity);

  return 0;
}

Resultados

Usando un tercer programa, generé un archivo de texto que contenía 33,280,276 números aleatorios. Los tiempos de ejecución son:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Cambiar la configuración de optimización del compilador no pareció cambiar mucho los resultados.

Por lo tanto: realmente hay una diferencia de velocidad.


EDITAR: El usuario clyfish señala a continuación que la diferencia de velocidad se debe en gran medida a que las funciones de E / S iostream mantienen la sincronización con las funciones de CI / O. Podemos desactivar esto con una llamada a std::ios::sync_with_stdio(false);:

#include <iostream>

int main(int argc, char **argv) {

  int parity = 0;
  int x;

  std::ios::sync_with_stdio(false);

  while (std::cin >> x)
    parity ^= x;
  std::cout << parity << std::endl;

  return 0;
}

Nuevos resultados:

iostream version:                       21.9 seconds
scanf version:                           6.8 seconds
iostream with sync_with_stdio(false):    5.5 seconds

¡C ++ iostream gana! Resulta que esta sincronización / descarga interna es lo que normalmente ralentiza la E / S de iostream. Si no estamos mezclando stdio e iostream, podemos desactivarlo y luego iostream es más rápido.

El código: https://gist.github.com/3845568

nibot
fuente
66
Creo que el uso de 'endl' puede ralentizar la ejecución.
Krishna Mohan
2
El uso de std :: endl no está en el bucle.
nibot
No hay diferencia con la sincronización activada o desactivada. Culpa a libc ++ por eso. Solo aumenta libstdc ++
iBug
¿Crees que habría alguna diferencia entre <cstdio> y <stdio.h> ??
Chandrahas Aroori
iostreampierde cuando analiza más de un entero en una scanfllamada.
Maxim Egorushkin
68

http://www.quora.com/Is-cin-cout-slower-than-scanf-printf/answer/Aditya-Vishwakarma

El rendimiento de cin/ coutpuede ser lento porque necesitan mantenerse sincronizados con la biblioteca C subyacente. Esto es esencial si se van a utilizar tanto C IO como C ++ IO.

Sin embargo, si solo va a usar C ++ IO, simplemente use la línea a continuación antes de cualquier operación de IO.

std::ios::sync_with_stdio(false);

Para obtener más información al respecto, consulte los documentos libstdc ++ correspondientes .

clyfish
fuente
Acabo de marcar la línea de arriba (std :: ios :: sync_with_stdio (false);) Y realmente hace que iostream sea casi tan rápido como cstdio
gabrielhidasy
también use cin.tie (static_cast <ostream *> (0)); para un mejor rendimiento
Mohamed El-Nakib
42

Probablemente scanf sea algo más rápido que usar streams. Aunque las transmisiones brindan mucha seguridad de tipo y no tienen que analizar cadenas de formato en tiempo de ejecución, generalmente tiene la ventaja de no requerir asignaciones de memoria excesivas (esto depende de su compilador y tiempo de ejecución). Dicho esto, a menos que el rendimiento sea su único objetivo final y esté en la ruta crítica, entonces debería favorecer los métodos más seguros (más lentos).

Hay una muy delicioso artículo escrito aquí por Herb Sutter " Los formateadores de Cuerda de Manor Farm " que entra en muchos detalles del funcionamiento de formateadores de cuerda como sscanfy lexical_casty qué tipo de cosas estaban haciendo correr lentamente o rápidamente. Esto es un poco análogo, probablemente al tipo de cosas que afectarían el rendimiento entre el estilo C IO y el estilo C ++. La principal diferencia con los formateadores tendía a ser el tipo de seguridad y el número de asignaciones de memoria.

1800 INFORMACIÓN
fuente
19

Acabo de pasar una tarde trabajando en un problema en UVa Online (Factovisores, un problema muy interesante, échale un vistazo):

http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=35&page=show_problem&problem=1080

Estaba recibiendo TLE (límite de tiempo excedido) en mis envíos. En estos sitios de jueces de resolución de problemas en línea, tiene un límite de tiempo de 2-3 segundos para manejar potencialmente miles de casos de prueba utilizados para evaluar su solución. Para problemas computacionalmente intensivos como este, cada microsegundo cuenta.

Estaba usando el algoritmo sugerido (leí en los foros de discusión del sitio), pero todavía recibía TLE.

Cambié solo "cin >> n >> m" a "scanf ("% d% d ", & n, & m)" y los pequeños "couts" pequeños a "printfs", ¡y mi TLE se convirtió en "Aceptado"!

Entonces, sí, puede hacer una gran diferencia, especialmente cuando los límites de tiempo son cortos.

Bogatyr
fuente
De acuerdo. Lo mismo me sucedió en UVA Online Judge problem: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib
6

Si le interesan tanto el rendimiento como el formato de cadenas, eche un vistazo a la biblioteca FastFormat de Matthew Wilson .

editar - enlace a la publicación accu en esa biblioteca: http://accu.org/index.php/journals/1539

xtofl
fuente
De acuerdo completamente. Pero debe tener en cuenta que FastFormat es solo para la salida. No tiene facilidades de entrada / lectura. (Todavía no, de todos modos)
dcw
Desafortunadamente ese enlace parece estar muerto. Aquí hay una copia de Wayback Machine: web.archive.org/web/20081222164527/http://fastformat.org
nibot
2

Hay implementaciones stdio ( libio ) que implementa FILE * como un streambuf de C ++ y fprintf como un analizador de formato de tiempo de ejecución. IOstreams no necesita el análisis del formato de tiempo de ejecución, todo eso se realiza en tiempo de compilación. Entonces, con los backends compartidos, es razonable esperar que iostreams sea más rápido en tiempo de ejecución.

MSalters
fuente
No lo creo. Creo que la libc de GNU es pura C y ensamblaje.
Chris Lutz
2

Sí, iostream es más lento que cstdio.
Sí, probablemente no deberías usar cstdio si estás desarrollando en C ++.
Dicho esto, hay formas aún más rápidas de obtener E / S que scanf si no le importa el formateo, escriba seguridad, bla, bla, bla ...

Por ejemplo, esta es una rutina personalizada para obtener un número de STDIN:

inline int get_number()
{
    int c;        
    int n = 0;

    while ((c = getchar_unlocked()) >= '0' && c <= '9')
    {
        // n = 10 * n + (c - '0');
        n = (n << 3) + ( n << 1 ) + c - '0';
    }
    return n;
}
pedro.lupin
fuente
1
getchar_unlocked () no es estándar y está disponible para gcc no visual studio
Mohamed El-Nakib
1

El problema es que cinconlleva una gran sobrecarga porque le brinda una capa de abstracción por encima de las scanf()llamadas. No debe usar scanf()más cinsi está escribiendo software C ++ porque eso es lo que quiere cin. Si desea rendimiento, probablemente no estaría escribiendo E / S en C ++ de todos modos.

dreamlax
fuente
2
¿Es cinrealmente más "abstracto" (en tiempo de ejecución) que scanf? No lo creo ... scanfdebe interpretar la cadena de formato en tiempo de ejecución, mientras que iostreamconoce el formato en tiempo de compilación.
nibot
1
@nibot: el tipo se conoce en tiempo de compilación pero no el formato . Si se espera que la entrada sea hexadecimal o no, por ejemplo, depende completamente de cómo std::istreamse configura en tiempo de ejecución (a través de manipuladores de E / S o mediante la configuración de indicadores en el istreampropio objeto). Un FILE*objeto, por otro lado, no tiene ese estado, por lo que una llamada a scanfeste respecto es mucho más estable.
dreamlax
1

Las declaraciones ciny coutde uso general parecen ser más lento que scanfy printfen C ++, pero en realidad son más rápidos!

La cuestión es: en C ++, cada vez que usa ciny cout, se lleva a cabo un proceso de sincronización de forma predeterminada que asegura que si usa ambos scanfy cinen su programa, ambos trabajan sincronizados entre sí. Este proceso de sincronización lleva tiempo. Por lo tanto, ciny coutparece ser más lento.

Sin embargo, si el proceso de sincronización está configurado para no ocurrir, cines más rápido que scanf.

Para omitir el proceso de sincronización, incluya el siguiente fragmento de código en su programa justo al comienzo de main():

std::ios::sync_with_stdio(false);

Visite este sitio para más información.

Prasoon Varshney
fuente
+1 para su explicación sobre la sincronización. Acababa de desactivar la sincronización y usé scanf y cin en algún código . ahora sé lo que estaba mal con eso. ¡gracias!
Dariush
1
#include <stdio.h>
#include <unistd.h>

#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

static int scanuint(unsigned int* x)
{
  char c;
  *x = 0;

  do
  {
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while(c<'0' || c>'9');

  do
  {
      //*x = (*x<<3)+(*x<<1) + c - '0';
      *x = 10 * (*x) + c - '0';
      c = getchar_unlocked();
      if (unlikely(c==EOF)) return 1;
  } while ((c>='0' && c<='9'));

  return 0;
}

int main(int argc, char **argv) {

  int parity = 0;
  unsigned int x;

  while (1 != (scanuint(&x))) {
    parity ^= x;
  }
  parity ^=x;
  printf("%d\n", parity);

  return 0;
}

Hay un error al final del archivo, pero este código C es dramáticamente más rápido que la versión C ++ más rápida.

paradox@scorpion 3845568-78602a3f95902f3f3ac63b6beecaa9719e28a6d6  make test        
time ./xor-c < rand.txt
360589110

real    0m11,336s
user    0m11,157s
sys 0m0,179s
time ./xor2-c < rand.txt
360589110

real    0m2,104s
user    0m1,959s
sys 0m0,144s
time ./xor-cpp < rand.txt
360589110

real    0m29,948s
user    0m29,809s
sys 0m0,140s
time ./xor-cpp-noflush < rand.txt
360589110

real    0m7,604s
user    0m7,480s
sys 0m0,123s

El C ++ original tardó 30 segundos, el código C tardó 2 segundos.

hexec
fuente
-1

Por supuesto, es ridículo usar cstdio sobre iostream. Al menos cuando desarrollas un software (si ya estás usando c ++ sobre c, entonces sigue todo el camino y usa sus beneficios en lugar de solo sufrir sus desventajas).

¡Pero en el juez en línea no está desarrollando software, está creando un programa que debería poder hacer cosas que el software de Microsoft tarda 60 segundos en lograr en 3 segundos!

Entonces, en este caso, la regla de oro es la siguiente (por supuesto, si no se mete en más problemas al usar Java)

  • Use c ++ y use todo su poder (y pesadez / lentitud) para resolver el problema
  • Si tiene tiempo limitado, cambie los cins y couts para printfs y scanfs (si se equivoca usando la cadena de clase, imprima así: printf (% s, mystr.c_str ());
  • Si todavía tiene tiempo limitado, intente hacer algunas optimizaciones obvias (como evitar demasiadas funciones integradas para / while / dowhiles o recursivas). También asegúrese de pasar por objetos de referencia que sean demasiado grandes ...
  • Si todavía tiene tiempo limitado, intente cambiar std :: vectors y sets para c-arrays.
  • Si todavía tiene tiempo limitado, continúe con el siguiente problema ...
Carlos Pacheco
fuente
-2

Incluso si scanffuera más rápido que cin, no importaría. La gran mayoría de las veces, leerá desde el disco duro o el teclado. Obtener los datos sin procesar en su aplicación requiere órdenes de magnitud más tiempo del que toma scanfo cinprocesarlos.

Jay Conrod
fuente
¿Qué pasa con IPC a través de tuberías? ¿Crees que podría haber un éxito notable rendimiento allí?
dreamlax
Incluso con IPC a través de tuberías, se pasa mucho más tiempo entrando y saliendo del kernel que simplemente analizarlo con scanf / cin.
Jay Conrod
8
Hice pruebas en esta área, y sin duda cout & cin suck performance. Si bien la entrada del usuario es insignificante, ciertamente no lo es para las cosas donde el rendimiento es importante. Sin embargo, existen otros marcos de c ++ que son más rápidos.
Johannes Schaub - litb
El problema es que iostream es más lento que el disco duro. Sí, apesta tanto.
polkovnikov.ph