Die Verwendung von scanf () in C ++ - Programmen ist schneller als die Verwendung von cin?

126

Ich weiß nicht, ob dies der Fall ist, aber als ich FAQ auf einer der Problembereitstellungsseiten las, fand ich etwas, das meine Aufmerksamkeit auf sich zog:

Überprüfen Sie Ihre Eingabe- / Ausgabemethoden. In C ++ ist die Verwendung von cin und cout zu langsam. Verwenden Sie diese, und Sie werden garantiert kein Problem mit einer angemessenen Menge an Eingabe oder Ausgabe lösen können. Verwenden Sie stattdessen printf und scanf.

Kann das bitte jemand klären? Ist die Verwendung von scanf () in C ++ - Programmen wirklich schneller als die Verwendung von cin >> ? Wenn ja, ist es eine gute Praxis, es in C ++ - Programmen zu verwenden? Ich dachte, dass es C-spezifisch ist, obwohl ich gerade C ++ lerne ...

zeroDivisible
quelle
14
Meine Vermutung: Ein schlechter Programmierer macht Standardbibliotheken für schlechte Leistung verantwortlich. Ein bisschen wie der immer humorvolle Schrei "Ich glaube, ich habe einen Fehler im GCC gefunden".
John Kugelman
11
@eclipse: Die ACM-Probleme, an denen ich für Wettbewerbe gearbeitet habe, haben einen erheblichen Input / Output und Ihr Programm muss die Fragen in weniger als 60 Sekunden lösen ... es wird hier zu einem echten Problem.
Mpen
19
--- das heißt, wenn Sie sich für diesen zusätzlichen Leistungsschub auf scanf () verlassen müssen, gehen Sie das Problem falsch an :)
mpen
4
Nur als Beobachtung - ich habe damit herumgespielt und bei den zweiten Problemen (PRIME1) - beide Male denselben Algorithmus verwendet, einmal mit cin / cout und einmal mit scanf / printf, und die erste Version war schneller als die zweite (aber nah genug, dass es statistisch irrelevant ist). Dies ist eines der Probleme, die als eingabe- / ausgabeintensiv gekennzeichnet sind, und die Eingabe- / Ausgabemethode machte keinerlei statistische Unterschiede.
Eclipse
4
@Eclipse - danke für die Informationen zum Testen beider Methoden. Ich bin zwar traurig - ich habe versucht, cin und cout zu beschuldigen, aber jetzt weiß ich, dass mein Algorithmus
scheiße ist

Antworten:

209

Hier ist ein kurzer Test eines einfachen Falls: Ein Programm zum Lesen einer Liste von Zahlen aus der Standardeingabe und XOR aller Zahlen.

iostream-Version:

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

scanf version:

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

Ergebnisse

Mit einem dritten Programm habe ich eine Textdatei mit 33.280.276 Zufallszahlen erstellt. Die Ausführungszeiten sind:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

Das Ändern der Optimierungseinstellungen des Compilers schien die Ergebnisse überhaupt nicht zu ändern.

Also: Es gibt wirklich einen Geschwindigkeitsunterschied.


BEARBEITEN : User Clyfish weist unten darauf hin, dass der Geschwindigkeitsunterschied hauptsächlich auf die Iostream-E / A-Funktionen zurückzuführen ist, die die Synchronisation mit den CI / O-Funktionen aufrechterhalten. Wir können dies mit einem Anruf ausschalten 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;
}

Neue Ergebnisse:

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

C ++ iostream gewinnt! Es stellt sich heraus, dass diese interne Synchronisierung / Spülung normalerweise die Iostream-E / A verlangsamt. Wenn wir nicht stdio und iostream mischen, können wir es ausschalten, und dann ist iostream am schnellsten.

Der Code: https://gist.github.com/3845568

Nibot
quelle
6
Ich denke, die Verwendung von 'endl' kann die Ausführung verlangsamen.
Krishna Mohan
2
Die Verwendung von std :: endl ist nicht in der Schleife.
Nibot
Macht beim Ein- und Ausschalten der Synchronisierung keinen Unterschied. Schuld daran ist libc ++. Es steigert nur libstdc ++
iBug
Glauben Sie, dass es einen Unterschied zwischen <cstdio> und <stdio.h> geben würde?
Chandrahas Aroori
iostreamverliert, wenn Sie mehr als eine Ganzzahl in einem scanfAufruf analysieren .
Maxim Egorushkin
68

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

Die Leistung von cin/ coutkann langsam sein, da sie sich mit der zugrunde liegenden C-Bibliothek synchronisieren müssen. Dies ist wichtig, wenn sowohl C IO als auch C ++ IO verwendet werden sollen.

Wenn Sie jedoch nur C ++ - E / A verwenden möchten, verwenden Sie einfach die folgende Zeile, bevor Sie E / A-Vorgänge ausführen.

std::ios::sync_with_stdio(false);

Weitere Informationen hierzu finden Sie in den entsprechenden libstdc ++ - Dokumenten .

Clyfish
quelle
Habe gerade die obige Zeile überprüft (std :: ios :: sync_with_stdio (false);) Und es macht iostream wirklich fast so schnell wie cstdio
gabrielhidasy
Verwenden Sie auch cin.tie (static_cast <ostream *> (0)). für eine bessere Leistung
Mohamed El-Nakib
42

Wahrscheinlich ist scanf etwas schneller als die Verwendung von Streams. Obwohl Streams viel Typensicherheit bieten und zur Laufzeit keine Formatzeichenfolgen analysieren müssen, hat dies normalerweise den Vorteil, dass keine übermäßigen Speicherzuweisungen erforderlich sind (dies hängt von Ihrem Compiler und Ihrer Laufzeit ab). Das heißt, wenn Leistung nicht Ihr einziges Endziel ist und Sie sich auf dem kritischen Pfad befinden, sollten Sie die sichereren (langsameren) Methoden wirklich bevorzugen.

Es ist ein sehr wohlschmeckender Artikel geschrieben hier von Herb Sutter „ Die String Formatter von Manor Farm “ , die in vielen Details der Leistung des String Formatierer gehen wie sscanfund lexical_castund was für Dinge machte sie laufen langsam oder schnell. Dies ist eine Art Analogie, wahrscheinlich zu den Dingen, die die Leistung zwischen IO im C-Stil und C ++ - Stil beeinträchtigen würden. Der Hauptunterschied zu den Formatierern bestand in der Typensicherheit und der Anzahl der Speicherzuordnungen.

1800 INFORMATIONEN
quelle
19

Ich habe gerade einen Abend damit verbracht, an einem Problem bei UVa Online zu arbeiten (Factovisors, ein sehr interessantes Problem, probieren Sie es aus):

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

Ich habe TLE (Zeitlimit überschritten) für meine Einsendungen erhalten. Auf diesen Online-Richterseiten zur Problemlösung haben Sie ein Zeitlimit von 2-3 Sekunden, um potenziell Tausende von Testfällen zu bearbeiten, die zur Bewertung Ihrer Lösung verwendet werden. Bei rechenintensiven Problemen wie diesem zählt jede Mikrosekunde.

Ich habe den vorgeschlagenen Algorithmus verwendet (siehe in den Diskussionsforen für die Site), bekam aber immer noch TLEs.

Ich habe nur "cin >> n >> m" in "scanf ("% d% d ", & n, & m)" und die wenigen winzigen "couts" in "printfs" geändert, und mein TLE wurde zu "Accepted"!

Ja, es kann einen großen Unterschied machen, insbesondere wenn die Fristen kurz sind.

Bogatyr
quelle
Zustimmen. Das gleiche passierte mir im UVA Online Judge Problem: Army Buddies uva.onlinejudge.org/…
Mohamed El-Nakib
6

Wenn Sie sich sowohl für die Leistung als auch für die Formatierung von Zeichenfolgen interessieren, schauen Sie sich die FastFormat- Bibliothek von Matthew Wilson an .

edit - Link zur Accu-Veröffentlichung in dieser Bibliothek: http://accu.org/index.php/journals/1539

xtofl
quelle
Stimme voll und ganz zu. Sie müssen sich jedoch bewusst sein, dass FastFormat nur für die Ausgabe vorgesehen ist. Es gibt keine Eingabe- / Lesefunktionen. (
Jedenfalls
Leider scheint dieser Link tot zu sein. Hier ist eine Kopie der Wayback-Maschine: web.archive.org/web/20081222164527/http://fastformat.org
Nibot
2

Es gibt stdio-Implementierungen ( libio ), die FILE * als C ++ - Streambuf und fprintf als Parser im Laufzeitformat implementieren. IOstreams müssen nicht im Laufzeitformat analysiert werden. Dies geschieht alles zur Kompilierungszeit. Wenn die Backends gemeinsam genutzt werden, ist zu erwarten, dass iostreams zur Laufzeit schneller ist.

MSalters
quelle
Das glaube ich nicht. Ich denke, GNUs libc ist reines C und Assembler.
Chris Lutz
2

Ja, iostream ist langsamer als cstdio.
Ja, Sie sollten cstdio wahrscheinlich nicht verwenden, wenn Sie in C ++ entwickeln.
Allerdings gibt es noch schnellere Möglichkeiten, E / A zu erhalten als scanf, wenn Sie sich nicht für Formatierung, Typensicherheit, bla, bla, bla ... interessieren.

Dies ist beispielsweise eine benutzerdefinierte Routine, um eine Nummer von STDIN abzurufen:

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
quelle
1
getchar_unlocked () ist nicht Standard und verfügbar für gcc nicht Visual Studio
Mohamed El-Nakib
1

Das Problem ist, dass cinviel Overhead erforderlich ist, da Sie über Abständen eine Abstraktionsschicht erhalten scanf(). Sie sollten nicht scanf()over verwenden, cinwenn Sie C ++ - Software schreiben, da dies gewünscht cinist. Wenn Sie Leistung wünschen, würden Sie wahrscheinlich sowieso keine E / A in C ++ schreiben.

Dreamlax
quelle
2
Ist cinwirklich "abstrakter" (zur Laufzeit) als scanf? Ich denke nicht ... scanfmuss die Formatzeichenfolge zur Laufzeit interpretieren, während der iostreamdas Format zur Kompilierungszeit kennt.
Nibot
1
@nibot: Der Typ ist zur Kompilierungszeit bekannt, nicht jedoch das Format . Ob die Eingabe beispielsweise hexadezimal sein soll oder nicht, hängt ganz davon ab, wie sie zur Laufzeitstd::istream konfiguriert wird (über E / A-Manipulatoren oder durch Setzen von Flags für das istreamObjekt selbst). Ein FILE*Objekt hingegen hat keinen solchen Zustand, so dass ein Aufruf scanfin dieser Hinsicht viel stabiler ist.
Dreamlax
1

Die Anweisungen cinund die coutallgemeine Verwendung scheinen langsamer als scanfund printfin C ++ zu sein, aber tatsächlich sind sie SCHNELLER!

Die Sache ist die : In C ++, wenn Sie verwenden cinund coutein Synchronisationsvorgang erfolgt standardmäßig, der sicherstellt , dass , wenn Sie beide verwenden scanfund cinin Ihrem Programm, dann werden sie beide arbeiten synchron miteinander. Dieser Synchronisierungsvorgang benötigt Zeit. Daher cinund coutscheinen langsamer zu sein.

Wenn der Synchronisierungsprozess jedoch so eingestellt ist, dass er nicht stattfindet, cinist er schneller als scanf.

Um den Synchronisierungsvorgang zu überspringen, fügen Sie gleich zu Beginn des folgenden Codeausschnitts in Ihr Programm ein main():

std::ios::sync_with_stdio(false);

Besuchen Sie diese Website für weitere Informationen.

Prasoon Varshney
quelle
+1 für Ihre Erklärung zur Synchronisation. Ich hatte gerade die Synchronisierung deaktiviert und sowohl scanf als auch cin in einem Code verwendet . Jetzt weiß ich, was daran falsch war. Danke!
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;
}

Es gibt einen Fehler am Ende der Datei, aber dieser C-Code ist dramatisch schneller als die schnellere C ++ - Version.

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

Das ursprüngliche C ++ dauerte 30 Sekunden, der C-Code 2 Sekunden.

hexec
quelle
-1

Natürlich ist es lächerlich, cstdio über iostream zu verwenden. Zumindest wenn Sie Software entwickeln (wenn Sie bereits C ++ über C verwenden, gehen Sie den ganzen Weg und nutzen Sie die Vorteile, anstatt nur unter den Nachteilen zu leiden).

Aber in der Online-Jury, in der Sie keine Software entwickeln, erstellen Sie ein Programm, das in der Lage sein sollte, Dinge zu tun, die Microsoft-Software in 3 Sekunden in 60 Sekunden erreichen muss !!!

In diesem Fall lautet die goldene Regel also (natürlich, wenn Sie mit Java nicht noch mehr Probleme bekommen).

  • Verwenden Sie c ++ und nutzen Sie die gesamte Kraft (und Schwere / Langsamkeit), um das Problem zu lösen
  • Wenn Sie zeitlich begrenzt sind, ändern Sie die Cins und Couts für printfs und scanfs (wenn Sie mithilfe der Klassenzeichenfolge Fehler machen, drucken Sie wie folgt: printf (% s, mystr.c_str ());
  • Wenn Sie immer noch zeitlich begrenzt sind, versuchen Sie, einige offensichtliche Optimierungen vorzunehmen (z. B. zu viele eingebettete Funktionen für / while / dowhiles oder rekursive Funktionen zu vermeiden). Stellen Sie außerdem sicher, dass Sie an zu großen Referenzobjekten vorbeikommen ...
  • Wenn Sie immer noch zeitlich begrenzt sind, versuchen Sie, std :: -Vektoren und -Sätze für c-Arrays zu ändern.
  • Wenn Sie immer noch zeitlich begrenzt sind, fahren Sie mit dem nächsten Problem fort ...
Carlos Pacheco
quelle
-2

Selbst wenn scanfes schneller wäre als cin, wäre es egal. Die meiste Zeit lesen Sie von der Festplatte oder der Tastatur. Das Einfügen der Rohdaten in Ihre Anwendung dauert um Größenordnungen länger als das Verarbeiten scanfoder cinVerarbeiten.

Jay Conrod
quelle
Was ist mit IPC durch Rohre? Denken Sie, dass es dort einen spürbaren Leistungseinbruch geben könnte?
Dreamlax
Selbst mit IPC durch Pipes wird viel mehr Zeit für das Ein- und Aussteigen in den Kernel aufgewendet als nur für das Parsen mit scanf / cin.
Jay Conrod
8
Ich habe Tests in diesem Bereich durchgeführt und mit Sicherheit die Leistung von Cout & Cin Suck. Während es für Benutzereingaben vernachlässigbar ist, ist es für Dinge, bei denen die Leistung wichtig ist, sicherlich nicht so. Es gibt jedoch auch andere C ++ - Frameworks, die schneller sind.
Johannes Schaub - litb
Das Problem ist, dass iostream es langsamer als HDD ist. Ja, es ist so scheiße.
polkovnikov.ph