mélanger cout et printf pour une sortie plus rapide

Après avoir effectué quelques tests, j’ai remarqué que printf est beaucoup plus rapide que cout . Je sais que cela dépend de la mise en œuvre, mais sur ma printf Linux, printf est 8 fois plus rapide. Donc, mon idée est de combiner les deux méthodes d’impression: je veux utiliser cout pour les impressions simples, et je prévois d’utiliser printf pour produire des sorties énormes (généralement en boucle). Je pense que c’est sûr de faire aussi longtemps que je n’oublie pas de vider avant de passer à l’autre méthode:

 cout << "Hello" << endl; cout.flush(); for (int i=0; i<1000000; ++i) { printf("World!\n"); } fflush(stdout); cout << "last line" << endl; cout << flush; 

Est-ce que ça va comme ça?

Mise à jour: Merci pour tous les précieux commentaires. Résumé des réponses: si vous voulez éviter les solutions délicates, il suffit de ne pas utiliser endl avec cout car il vide le tampon implicitement. Utilisez "\n" place. Cela peut être intéressant si vous produisez de gros résultats.

La réponse directe est que oui, ça va.

Beaucoup de gens se sont penchés sur différentes idées pour améliorer la vitesse, mais il semble y avoir beaucoup de désaccord sur ce qui est le plus efficace. J’ai décidé d’écrire un programme de test rapide pour avoir au moins une idée des techniques utilisées.

 #include  #include  #include  #include  #include  #include  #include  #include  char fmt[] = "%s\n"; static const int count = 3000000; static char const *const ssortingng = "This is a ssortingng."; static std::ssortingng s = std::ssortingng(ssortingng) + "\n"; void show_time(void (*f)(), char const *caption) { clock_t start = clock(); f(); clock_t ticks = clock()-start; std::cerr << std::setw(30) << caption << ": " << (double)ticks/CLOCKS_PER_SEC << "\n"; } void use_printf() { for (int i=0; i(std::cout, "\n"), count, ssortingng); } void use_write() { for (int i = 0; i < count; i++) std::cout.write(s.data(), s.size()); } int main() { show_time(use_printf, "Time using printf"); show_time(use_puts, "Time using puts"); show_time(use_cout, "Time using cout (synced)"); show_time(use_cout_unsync, "Time using cout (un-synced)"); show_time(use_stringstream, "Time using stringstream"); show_time(use_endl, "Time using endl"); show_time(use_fill_n, "Time using fill_n"); show_time(use_write, "Time using write"); return 0; } 

Je l'ai exécuté sous Windows après la compilation avec VC ++ 2013 (versions x86 et x64). La sortie d'une exécution (avec une sortie redirigée vers un fichier disque) ressemblait à ceci:

  Time using printf: 0.953 Time using puts: 0.567 Time using cout (synced): 0.736 Time using cout (un-synced): 0.714 Time using ssortingngstream: 0.725 Time using endl: 20.097 Time using fill_n: 0.749 Time using write: 0.499 

Comme prévu, les résultats varient, mais je trouve certains points intéressants:

  1. printf / met est beaucoup plus rapide que cout lors de l'écriture sur le périphérique NUL
    • mais cout se maintient bien en écrivant dans un vrai fichier
  2. Un certain nombre d’optimisations proposées accomplissent peu
    • Dans mes tests, fill_n est aussi rapide que toute autre chose
  3. De loin la plus grande optimisation évite endl
  4. cout.write a donné le temps le plus rapide (mais probablement pas de manière significative

J'ai récemment modifié le code pour forcer un appel à printf . Anders Kaseorg a eu la gentillesse de souligner - que g++ reconnaît la séquence spécifique printf("%s\n", foo); est équivalent à puts(foo); , et génère du code en conséquence (c.-à-d., génère du code pour appeler les puts au lieu de printf ). Déplacer la chaîne de format vers un tableau global et la transmettre car la chaîne de format produit une sortie identique, mais la force à être produite via printf au lieu de puts . Bien sûr, il est possible qu’ils optimisent ce système un jour, mais au moins pour le moment (g ++ 5.1) un test avec g++ -O3 -S confirme qu’il appelle réellement printf (où le code précédent compilé pour un appel .

L’envoi de std::endl dans le stream ajoute une newline et vide le stream. L’invocation ultérieure de cout.flush() est superflue. Si cela était fait lors du chronométrage des cout rapport à printf vous ne compariez pas les pommes aux pommes.

Par défaut, les stream de sortie standard C et C ++ sont synchronisés, de sorte que l’écriture sur l’un d’eux provoque un vidage de l’autre, les vidages explicites ne sont donc pas nécessaires.

Notez également que le stream C ++ est synchronisé avec le stream C.
Ainsi, il rest du travail supplémentaire pour restr synchronisé.

Une autre chose à noter est de s’assurer que vous vider les stream d’un montant égal. Si vous videz continuellement le stream sur un système et pas sur l’autre, cela affectera certainement la vitesse des tests.

Avant de supposer que l’un est plus rapide que l’autre, vous devriez:

  • désynchroniser les E / S C ++ à partir du CI / O (voir sync_with_stdio ()).
  • Assurez-vous que la quantité de rinçage est comparable.

Vous pouvez encore améliorer les performances de printf en augmentant la taille du tampon pour stdout :

 setvbuf (stdout, NULL, _IOFBF, 32768); // any value larger than 512 and also a // a multiple of the system i/o buffer size is an improvement 

Le nombre d’appels au système d’exploitation pour effectuer des E / S est presque toujours le composant le plus coûteux et le limiteur de performances.

Bien sûr, si la sortie de cout est mélangée avec stdout , les vidages de mémoire tampon vont à l’encontre de l’objective d’une taille de mémoire tampon accrue.

Vous pouvez utiliser sync_with_stdio pour rendre C ++ IO plus rapide.

 cout.sync_with_stdio(false); 

Devrait améliorer votre performance de sortie avec cout .

Ne vous souciez pas des performances entre printf et cout . Si vous souhaitez gagner en performance, séparez la sortie formatée de la sortie non formatée.

puts("Hello World\n") est beaucoup plus rapide que printf("%s", "Hellow World\n") . (Principalement en raison de la surcharge de formatage). Une fois que vous avez isolé le formatage du texte brut, vous pouvez faire des trucs comme:

 const char hello[] = "Hello World\n"; cout.write(hello, sizeof(hello) - sizeof('\0')); 

Pour accélérer la sortie formatée, l’astuce consiste à effectuer tout le formatage d’une chaîne, puis à utiliser la sortie de bloc avec la chaîne (ou le tampon):

 const unsigned int MAX_BUFFER_SIZE = 256; char buffer[MAX_BUFFER_SIZE]; sprintf(buffer, "%d times is a charm.\n", 5); unsigned int text_length = strlen(buffer) - sizeof('\0'); fwrite(buffer, 1, text_length, stdout); 

Pour améliorer encore les performances de votre programme, réduisez la quantité de sortie. Moins vous produirez de contenu, plus votre programme sera rapide. Un effet secondaire sera que la taille de votre exécutable diminuera également.

Eh bien, je ne peux penser à aucune raison d’utiliser le cout pour être honnête. Il est complètement fou d’avoir un énorme modèle volumineux pour faire quelque chose de si simple qui sera dans chaque fichier. En outre, c’est comme si elle était conçue pour être aussi lente à taper que possible et après la millionième fois que vous tapez <<<<, puis que vous saisissez la valeur entre et obtenez quelque chose lik> NomVariable >>> en cas d’accident. .

Sans compter que si vous incluez le std namespace, le monde finira par imploser, et si vous ne le faites pas, votre fardeau de frappe deviendra encore plus ridicule.

Cependant, je n’aime pas beaucoup printf non plus. Pour moi, la solution consiste à créer ma propre classe concrète, puis à appeler tout ce qui est nécessaire. Ensuite, vous pouvez avoir io très simple de la manière que vous voulez et avec n’importe quelle implémentation, quel que soit le format que vous voulez, etc. (généralement, les flottants ne sont pas un moyen de les formater sans raison. dans le formatage à chaque appel est une blague).

Donc, tout ce que je tape est quelque chose comme dout + “Ceci est plus sain que” + cPlusPlusMethod + “de” + debugIoType + “. IMO au moins”; dout ++;

mais vous pouvez avoir tout ce que vous voulez. Avec beaucoup de fichiers, il est surprenant que cela améliore aussi le temps de compilation.

De plus, il n’ya rien de mal à mélanger C et C ++, il faut juste le faire avec délicatesse et si vous utilisez les choses qui causent des problèmes lors de l’utilisation de C, le moindre de vos soucis est de mélanger C et C. C ++.

Le mélange de méthodes C ++ et C est recommandé par mes livres C ++, FYI. Je suis presque sûr que les fonctions C piétinent l’état attendu / détenu par C ++.