L'utilisation de scanf () dans les programmes C ++ est plus rapide que l'utilisation de cin?

126

Je ne sais pas si cela est vrai, mais quand je lisais la FAQ sur l'un des problèmes de fourniture de sites, j'ai trouvé quelque chose qui a attiré mon attention:

Vérifiez vos méthodes d'entrée / sortie. En C ++, l'utilisation de cin et cout est trop lente. Utilisez-les et vous garantissez de ne pouvoir résoudre aucun problème avec une quantité décente d'entrée ou de sortie. Utilisez plutôt printf et scanf.

Quelqu'un peut-il clarifier cela? Est-ce vraiment plus rapide d' utiliser scanf () dans les programmes C ++ que d'utiliser cin >> quelque chose ? Si oui, est-ce une bonne pratique de l'utiliser dans les programmes C ++? Je pensais que c'était spécifique au C, même si j'apprends juste le C ++ ...

zeroDivisible
la source
14
Ma supposition: un mauvais programmeur blâme les bibliothèques standard pour de mauvaises performances. Un peu comme le cri toujours humoristique "Je pense que j'ai trouvé un bug dans GCC".
John Kugelman
11
@eclipse: les problèmes ACM sur lesquels j'ai travaillé pour les compétitions ont une quantité importante d'entrées / sorties et votre programme doit résoudre les questions en moins de 60 secondes ... cela devient un vrai problème ici.
mpen
19
--- cela dit, si vous avez besoin de compter sur scanf () pour augmenter les performances supplémentaires, vous
traitez
4
Juste comme observation - j'ai joué avec, et sur le 2ème problème (PRIME1) - en utilisant le même algorithme, les deux fois, une fois avec cin / cout et une fois avec scanf / printf et la première version était plus rapide que la seconde (mais assez proche pour que ce soit statistiquement non pertinent). C'est l'un des problèmes marqués comme exigeant beaucoup d'entrées / sorties, et la méthode d'entrées / sorties n'a fait aucune différence statistique.
Eclipse
4
@Eclipse - merci pour les informations sur le test des deux méthodes. Je suis triste cependant - j'ai essayé de blâmer cin et cout, mais maintenant je sais que mon algorithme est nul :)
zeroDivisible

Réponses:

209

Voici un test rapide d'un cas simple: un programme pour lire une liste de nombres à partir d'une entrée standard et XOR tous les nombres.

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

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

Résultats

En utilisant un troisième programme, j'ai généré un fichier texte contenant 33 280 276 nombres aléatoires. Les délais d'exécution sont:

iostream version:  24.3 seconds
scanf version:      6.4 seconds

La modification des paramètres d'optimisation du compilateur ne semble pas du tout modifier les résultats.

Donc: il y a vraiment une différence de vitesse.


EDIT: l' utilisateur clyfish souligne ci - dessous que la différence de vitesse est en grande partie due aux fonctions d'E / S iostream maintenant la synchronisation avec les fonctions CI / O. Nous pouvons désactiver cela en appelant à 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;
}

Nouveaux résultats:

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

C ++ iostream gagne! Il s'avère que cette synchronisation / vidage interne est ce qui ralentit normalement les entrées / sorties iostream. Si nous ne mélangeons pas stdio et iostream, nous pouvons le désactiver, puis iostream est le plus rapide.

Le code: https://gist.github.com/3845568

nibot
la source
6
Je pense que l'utilisation de «endl» peut ralentir l'exécution.
Krishna Mohan
2
L'utilisation de std :: endl n'est pas dans la boucle.
nibot
Cela ne fait aucune différence avec la synchronisation activée ou désactivée. Blâmez libc ++ pour cela. Il ne fait que
booster
Pensez-vous qu'il y aurait une différence entre <cstdio> et <stdio.h> ??
Chandrahas Aroori
iostreamperd lorsque vous analysez plus d'un entier en un seul scanfappel.
Maxim Egorushkin
68

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

Les performances de cin/ coutpeuvent être lentes, car elles doivent rester synchronisées avec la bibliothèque C sous-jacente. Ceci est essentiel si C IO et C ++ IO doivent être utilisés.

Cependant, si vous n'utilisez que C ++ IO, utilisez simplement la ligne ci-dessous avant toute opération IO.

std::ios::sync_with_stdio(false);

Pour plus d'informations à ce sujet, consultez la documentation libstdc ++ correspondante .

clyfish
la source
Je viens de vérifier la ligne ci-dessus (std :: ios :: sync_with_stdio (false);) Et ça rend vraiment iostream presque aussi rapide que cstdio
gabrielhidasy
utilisez également cin.tie (static_cast <ostream *> (0)); pour une meilleure performance
Mohamed El-Nakib
42

Scanf est probablement un peu plus rapide que l'utilisation de flux. Bien que les flux fournissent une grande sécurité de type et ne nécessitent pas d'analyser les chaînes de format lors de l'exécution, ils ont généralement l'avantage de ne pas nécessiter d'allocations de mémoire excessives (cela dépend de votre compilateur et de votre runtime). Cela dit, à moins que la performance ne soit votre seul objectif final et que vous soyez dans le chemin critique, vous devriez vraiment privilégier les méthodes les plus sûres (plus lentes).

Il y a un très délicieux article écrit ici par Herb Sutter " Les formateurs de cordes de Manor Farm " qui va dans beaucoup de détails sur les performances des formateurs de cordes comme sscanfet lexical_castet sur le genre de choses qui les faisaient fonctionner lentement ou rapidement. C'est un peu analogue, probablement au genre de choses qui affectent les performances entre les E / S de style C et le style C ++. La principale différence avec les formateurs était généralement la sécurité du type et le nombre d'allocations de mémoire.

1800 INFORMATIONS
la source
19

Je viens de passer une soirée à travailler sur un problème sur UVa Online (Factovisors, un problème très intéressant, vérifiez-le):

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

J'obtenais TLE (délai dépassé) sur mes soumissions. Sur ces sites de juges en ligne de résolution de problèmes, vous disposez d'un délai d'environ 2 à 3 secondes pour gérer potentiellement des milliers de cas de test utilisés pour évaluer votre solution. Pour les problèmes intensifs en calcul comme celui-ci, chaque microseconde compte.

J'utilisais l'algorithme suggéré (lu dans les forums de discussion du site), mais j'obtenais toujours des TLE.

J'ai changé juste "cin >> n >> m" en "scanf ("% d% d ", & n, & m)" et les quelques petits "couts" en "printfs", et mon TLE est devenu "Accepté"!

Donc, oui, cela peut faire une grande différence, surtout lorsque les délais sont courts.

Bogatyr
la source
Se mettre d'accord. La même chose m'est arrivée dans le problème du juge en ligne UVA: Army Buddies uva.onlinejudge.org
Mohamed El-Nakib
6

Si vous vous souciez à la fois des performances et du formatage des chaînes, jetez un œil à la bibliothèque FastFormat de Matthew Wilson .

edit - lien vers la publication accu sur cette bibliothèque: http://accu.org/index.php/journals/1539

xtofl
la source
Complètement d'accord. Mais vous devez être conscient que FastFormat est uniquement destiné à la sortie. Il n'a pas de fonctions d'entrée / lecture. (Pas encore, de toute façon)
dcw
Malheureusement, ce lien semble être mort. Voici une copie de Wayback Machine: web.archive.org/web/20081222164527/http://fastformat.org
nibot
2

Il existe des implémentations stdio ( libio ) qui implémentent FILE * en tant que streambuf C ++, et fprintf en tant qu'analyseur de format d'exécution. Les IOstream n'ont pas besoin d'analyse du format d'exécution, tout est fait au moment de la compilation. Ainsi, avec les backends partagés, il est raisonnable de s'attendre à ce que iostreams soit plus rapide à l'exécution.

MSalters
la source
Je ne pense pas. Je pense que la libc de GNU est un pur C et un assemblage.
Chris Lutz
2

Oui, iostream est plus lent que cstdio.
Oui, vous ne devriez probablement pas utiliser cstdio si vous développez en C ++.
Cela dit, il existe des moyens encore plus rapides d'obtenir des E / S que scanf si vous ne vous souciez pas du formatage, de la sécurité de type, bla, bla, bla ...

Par exemple, il s'agit d'une routine personnalisée pour obtenir un nombre 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
la source
1
getchar_unlocked () n'est pas standard et disponible pour gcc not visual studio
Mohamed El-Nakib
1

Le problème est que cela cinimplique beaucoup de frais généraux car cela vous donne une couche d'abstraction au scanf()- dessus des appels. Vous ne devriez pas utiliser scanf()over cinsi vous écrivez un logiciel C ++ parce que c'est pour cela que vous voulez cin. Si vous voulez des performances, vous n'écririez probablement pas d'E / S en C ++ de toute façon.

dreamlax
la source
2
Est-ce cinvraiment plus "abstrait" (à l'exécution) que scanf? Je ne pense pas ... scanfdoit interpréter la chaîne de format au moment de l'exécution, alors que le iostreamconnaît le format au moment de la compilation.
nibot
1
@nibot: Le type est connu au moment de la compilation mais pas le format . Le fait que l'entrée soit censée être hexadécimale ou non, par exemple, dépend entièrement de la façon dont le std::istreamest configuré au moment de l' exécution (via les manipulateurs d'E / S ou en définissant des indicateurs sur l' istreamobjet lui-même). Un FILE*objet en revanche n'a pas un tel état, donc un appel à scanfcet égard est beaucoup plus stable.
dreamlax
1

Les déclarations cinet coutà usage général semblent être plus lent que scanfet printfen C ++, mais en réalité ils sont plus rapides!

La chose est: en C ++, chaque fois que vous utilisez cinet cout, un processus de synchronisation a lieu par défaut qui garantit que si vous utilisez les deux scanfet cindans votre programme, ils fonctionnent tous les deux de manière synchronisée. Ce processus de synchronisation prend du temps. Par conséquent cinet coutSEMBLENT être plus lent.

Cependant, si le processus de synchronisation est défini pour ne pas se produire, cinest plus rapide que scanf.

Pour ignorer le processus de synchronisation, incluez l'extrait de code suivant dans votre programme au début de main():

std::ios::sync_with_stdio(false);

Visitez ce site pour plus d'informations.

Prasoon Varshney
la source
+1 pour votre explication sur la synchronisation. Je venais de désactiver la synchronisation et d'utiliser à la fois scanf et cin dans un certain code . maintenant je sais ce qui n'allait pas. Merci!
Dariush le
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;
}

Il y a un bogue à la fin du fichier, mais ce code C est considérablement plus rapide que la version C ++ plus rapide.

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

Le C ++ d'origine a pris 30 secondes, le code C a pris 2 secondes.

hexec
la source
-1

Bien sûr, il est ridicule d'utiliser cstdio sur iostream. Au moins lorsque vous développez un logiciel (si vous utilisez déjà c ++ sur c, allez jusqu'au bout et utilisez ses avantages au lieu de ne souffrir que de ses inconvénients).

Mais dans le juge en ligne, vous ne développez pas de logiciel, vous créez un programme qui devrait être capable de faire des choses que les logiciels Microsoft mettent 60 secondes à réaliser en 3 secondes !!!

Donc, dans ce cas, la règle d'or est la suivante (bien sûr, si vous n'obtenez pas encore plus de problèmes en utilisant java)

  • Utilisez C ++ et utilisez toute sa puissance (et sa lourdeur / sa lenteur) pour résoudre le problème
  • Si vous êtes limité dans le temps, modifiez les cins et les couts pour printfs et scanfs (si vous vous trompez en utilisant la chaîne de classe, imprimez comme ceci: printf (% s, mystr.c_str ());
  • Si vous êtes toujours limité dans le temps, essayez de faire des optimisations évidentes (comme éviter trop d'incorporés pour / while / dowhiles ou de fonctions récursives). Assurez-vous également de passer par des objets de référence trop gros ...
  • Si vous êtes toujours limité dans le temps, essayez de changer std :: vectors et sets pour c-arrays.
  • Si votre temps est toujours limité, passez au problème suivant ...
Carlos Pacheco
la source
-2

Même si scanfc'était plus rapide que cin, cela n'aurait pas d'importance. La grande majorité du temps, vous lirez à partir du disque dur ou du clavier. Obtenir les données brutes dans votre application prend des ordres de grandeur plus de temps qu'il n'en faut scanfou cinpour les traiter.

Jay Conrod
la source
Qu'en est-il de l'IPC via des tuyaux? Pensez-vous qu'il pourrait y avoir un impact notable sur les performances?
dreamlax
Même avec IPC via des tuyaux, il passe beaucoup plus de temps à entrer et à sortir du noyau que de simplement l'analyser avec scanf / cin.
Jay Conrod
8
J'ai fait des tests dans ce domaine, et certainement des performances de cout & cin. Alors que pour les entrées de l'utilisateur, c'est négligeable, ce n'est certainement pas le cas pour les choses où les performances sont importantes. Il existe cependant d'autres frameworks C ++ plus rapides.
Johannes Schaub - litb le
Le problème est qu'il iostream est plus lent que hdd. Oui, ça craint tant que ça.
polkovnikov.ph