L'uso di scanf () nei programmi C ++ è più veloce dell'uso di cin?


126

Non so se questo sia vero, ma quando stavo leggendo le FAQ su uno dei problemi di fornitura dei siti, ho trovato qualcosa che ha attirato la mia attenzione:

Controlla i tuoi metodi di input / output. In C ++, usare cin e cout è troppo lento. Utilizzali e garantirai di non essere in grado di risolvere qualsiasi problema con una discreta quantità di input o output. Utilizzare invece printf e scanf.

Qualcuno può chiarire questo? Usa davvero scanf () nei programmi C ++ più velocemente dell'uso di cin >> qualcosa ? Se sì, è una buona pratica usarlo nei programmi C ++? Ho pensato che fosse specifico per il C, anche se sto solo imparando il C ++ ...


14
La mia ipotesi: un cattivo programmatore incolpa le librerie standard per scarse prestazioni. Un po 'come il grido sempre divertente "Penso di aver trovato un bug in GCC".
John Kugelman,

11
@eclipse: i problemi di ACM su cui ho lavorato per le competizioni hanno una notevole quantità di input / output e il tuo programma deve risolvere le domande in meno di 60 secondi ... qui diventa un vero problema.
Aprire il

19
--- Detto questo, se hai bisogno di affidarti a scanf () per quel boost di prestazioni extra, stai per affrontare il problema nel modo sbagliato :)
mpen

4
Proprio come un'osservazione - ci ho giocato intorno e sul 2o problema (PRIME1) - usando lo stesso algoritmo, entrambe le volte, una volta usando cin / cout e una volta con scanf / printf e la prima versione era più veloce della seconda (ma abbastanza vicino da essere statisticamente irrilevante). Questo è uno dei problemi contrassegnati come ad alta intensità di input / output e il metodo di input / output non ha fatto alcuna differenza statistica.
Eclipse,

4
@Eclipse - grazie per le informazioni sul test di entrambi i metodi. Sono triste però - ho cercato di incolpare cin e cout, ma ora so che il mio algoritmo fa schifo :)
zero Divisibile

Risposte:


209

Ecco un breve test di un semplice caso: un programma per leggere un elenco di numeri dall'input standard e XOR tutti i numeri.

versione 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;
}

versione 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;
}

risultati

Utilizzando un terzo programma, ho generato un file di testo contenente 33.280.276 numeri casuali. I tempi di esecuzione sono:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

La modifica delle impostazioni di ottimizzazione del compilatore non sembra aver cambiato molto i risultati.

Quindi: c'è davvero una differenza di velocità.


EDIT: User clyfish indica di seguito che la differenza di velocità è in gran parte dovuta alle funzioni Iostream di Iostream che mantengono la sincronizzazione con le funzioni CI / O. Possiamo disattivarlo con una chiamata 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;
}

Nuovi risultati:

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

Vince C ++ iostream! Si scopre che questa sincronizzazione interna / flushing è ciò che normalmente rallenta l'Iostream i / o. Se non stiamo mescolando stdio e iostream, possiamo disattivarlo, quindi iostream è il più veloce.

Il codice: https://gist.github.com/3845568


6
Penso che l'uso di "endl" possa rallentare l'esecuzione.
Krishna Mohan,

2
L'uso di std :: endl non è nel ciclo.
nibot

Non fa alcuna differenza con la sincronizzazione attivata o disattivata. Dai la colpa a libc ++ per questo. Aumenta solo libstdc ++
iBug

Pensi che ci sarebbe qualche differenza tra <cstdio> e <stdio.h> ??
Chandrahas Aroori,

iostreamperde quando si analizza più di un numero intero in una scanfchiamata.
Maxim Egorushkin,

68

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

Le prestazioni di cin/ coutpossono essere lente perché devono essere sincronizzate con la libreria C sottostante. Ciò è essenziale se verranno utilizzati sia C IO che C ++ IO.

Tuttavia, se si intende utilizzare solo C ++ IO, utilizzare semplicemente la riga seguente prima di qualsiasi operazione IO.

std::ios::sync_with_stdio(false);

Per maggiori informazioni su questo, guarda i corrispondenti documenti libstdc ++ .


Ho appena controllato la riga sopra (std :: ios :: sync_with_stdio (false);) E rende davvero iostream quasi veloce come cstdio
gabrielhidasy,

usa anche cin.tie (static_cast <ostream *> (0)); per prestazioni migliori
Mohamed El-Nakib,

42

Probabilmente scanf è leggermente più veloce rispetto all'utilizzo di stream. Sebbene i flussi forniscano molta sicurezza del tipo e non debbano analizzare le stringhe di formato in fase di runtime, di solito ha il vantaggio di non richiedere allocazioni eccessive di memoria (questo dipende dal compilatore e dal runtime). Detto questo, a meno che le prestazioni non siano il tuo unico obiettivo finale e tu non sia nel percorso critico, dovresti davvero favorire i metodi più sicuri (più lenti).

C'è un delizioso articolo scritto qui da Herb Sutter " The String Formattatori di Manor Farm ", che va in un sacco di dettagli delle prestazioni dei formattatori stringa come sscanfe lexical_caste che tipo di cose stavano facendo correre lentamente o velocemente. Questo è analogo, probabilmente al tipo di cose che potrebbero influenzare le prestazioni tra IO stile C e stile C ++. La differenza principale con i formattatori tendeva ad essere la sicurezza del tipo e il numero di allocazioni di memoria.


19

Ho appena trascorso una serata lavorando su un problema su UVa Online (Factovisors, un problema molto interessante, dai un'occhiata):

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

Stavo ricevendo TLE (limite di tempo superato) per le mie comunicazioni. Su questi siti di risoluzione dei problemi online, hai un limite di tempo di circa 2-3 secondi per gestire potenzialmente migliaia di casi di test utilizzati per valutare la tua soluzione. Per problemi di calcolo intensivo come questo, ogni microsecondo conta.

Stavo usando l'algoritmo suggerito (leggi nei forum di discussione per il sito), ma stavo ancora ottenendo TLE.

Ho cambiato solo "cin >> n >> m" in "scanf ("% d% d ", & n, & m)" e i pochi piccoli "tagli" in "printfs", e il mio TLE è diventato "Accettato"!

Quindi sì, può fare una grande differenza, specialmente quando i termini sono brevi.


Essere d'accordo. Lo stesso è successo a me nel problema del giudice online UVA: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib,

6

Se ti interessa sia la formattazione delle stringhe che le prestazioni, dai un'occhiata alla libreria FastFormat di Matthew Wilson .

modifica - link alla pubblicazione accu su quella libreria: http://accu.org/index.php/journals/1539


Sono completamente d'accordo. Ma devi essere consapevole che FastFormat è solo per l'output. Non ha funzioni di input / lettura. (Non ancora, comunque)
dcw,

Sfortunatamente quel collegamento sembra essere morto. Ecco una copia di Wayback Machine: web.archive.org/web/20081222164527/http://fastformat.org
nibot

2

Esistono implementazioni stdio ( libio ) che implementano FILE * come streambuf C ++ e fprintf come parser di formato runtime. I flussi IO non necessitano di analisi del formato di runtime, tutto fatto in fase di compilazione. Quindi, con i backend condivisi, è ragionevole aspettarsi che iostreams sia più veloce in fase di esecuzione.


Io non la penso così. Penso che la libc di GNU sia pura C e assembly.
Chris Lutz,

2

Sì, iostream è più lento di cstdio.
Sì, probabilmente non dovresti usare cstdio se stai sviluppando in C ++.
Detto questo, ci sono modi ancora più veloci per ottenere I / O di scanf se non ti interessa la formattazione, digita safety, blah, blah, blah ...

Ad esempio questa è una routine personalizzata per ottenere un numero da 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;
}

1
getchar_unlocked () non è standard ed è disponibile per gcc not visual studio
Mohamed El-Nakib,

1

Il problema è che cinha un sacco di spese generali perché ti dà uno strato di astrazione sopra le scanf()chiamate. Non si deve usare scanf()nel corso cinse si sta scrivendo un software C ++, perché questo è mancanza cinè per. Se desideri prestazioni, probabilmente non scriverai I / O in C ++.


2
È cindavvero più "astratto" (in fase di esecuzione) di scanf? Non credo ... scanfdeve interpretare la stringa di formato in fase di esecuzione, mentre iostreamconosce il formato in fase di compilazione.
nibot

1
@nibot: il tipo è noto in fase di compilazione ma non nel formato . Ad esempio, se si prevede che l'input sia esadecimale o meno, dipende interamente da come std::istreamè configurato in fase di esecuzione (tramite manipolatori I / O o impostando flag istreamsull'oggetto stesso). Un FILE*oggetto d'altra parte non ha tale stato, quindi una chiamata a scanfquesto proposito è molto più stabile.
dreamlax,

1

Le dichiarazioni cine coutin generale sembrano essere più lente di scanfe printfin C ++, ma in realtà sono più veloci!

Il fatto è: in C ++, ogni volta che si utilizza cine cout, per impostazione predefinita si verifica un processo di sincronizzazione che assicura che se si utilizzano entrambi scanfe cinnel programma, entrambi funzionano in sincronia tra loro. Questo processo di sincronizzazione richiede tempo. Quindi cine coutASPETTO essere più lenti.

Tuttavia, se il processo di sincronizzazione non è impostato, cinè più veloce di scanf.

Per saltare il processo di sincronizzazione, includere il seguente frammento di codice nel programma all'inizio di main():

std::ios::sync_with_stdio(false);

Visita questo sito per ulteriori informazioni.


+1 per la tua spiegazione sulla sincronizzazione. Avevo appena disattivato la sincronizzazione e usato sia scanf che cin in qualche codice . ora so cosa c'era che non andava. grazie!
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;
}

C'è un bug alla fine del file, ma questo codice C è notevolmente più veloce della versione C ++ più veloce.

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

Il C ++ originale impiegava 30 secondi mentre il codice C impiegava 2 secondi.


-1

Ovviamente è ridicolo usare cstdio su iostream. Almeno quando sviluppi software (se stai già usando c ++ su c, vai fino in fondo e usa i suoi benefici invece di soffrire solo dei suoi svantaggi).

Ma nel giudice online non stai sviluppando software, stai creando un programma che dovrebbe essere in grado di fare cose che il software Microsoft impiega 60 secondi per raggiungere in 3 secondi !!!

Quindi, in questo caso, la regola d'oro va come (ovviamente se non ti trovi in ​​ulteriori problemi usando Java)

  • Usa c ++ e usa tutta la sua potenza (e pesantezza / lentezza) per risolvere il problema
  • Se hai un tempo limitato, cambia i cins e i cout per printfs e scanfs (se vieni rovinato usando la stringa di classe, stampa così: printf (% s, mystr.c_str ());
  • Se hai ancora un tempo limitato, prova a fare alcune ovvie ottimizzazioni (come evitare troppe funzioni incorporate per / while / dowhiles o ricorsive). Assicurati anche di passare per oggetti di riferimento troppo grandi ...
  • Se hai ancora tempo limitato, prova a cambiare std :: vettori e set per c-array.
  • Se hai ancora tempo limitato, passa al problema successivo ...

-2

Anche se scanffossero più veloci di cin, non importerebbe. La maggior parte delle volte leggerai dal disco rigido o dalla tastiera. Ottenere i dati grezzi nell'applicazione richiede ordini di grandezza più tempo di quanti ne richiedano scanfo cinper elaborarli.


Che dire dell'IPC attraverso i tubi? Pensi che potrebbe esserci un notevole successo di prestazioni lì?
dreamlax,

Anche con IPC tramite pipe, molto più tempo viene passato dentro e fuori dal kernel piuttosto che analizzarlo con scanf / cin.
Jay Conrod,

8
Ho fatto dei test in questo settore, e sicuramente cout & cin succhia le prestazioni. Mentre per l'input dell'utente è trascurabile, non lo è certamente per le cose in cui le prestazioni sono importanti. Esistono altri framework c ++ che sono più veloci, però.
Johannes Schaub - litb

Il problema è che iostream è più lento di HDD. Sì, fa così schifo.
polkovnikov.ph
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.