Après avoir effectué quelques tests, j'ai remarqué que printf
est beaucoup plus rapide que cout
. Je sais que cela dépend de l'implémentation, mais sur ma machine Linux, la variable printf
est 8 fois plus rapide. Mon idée est donc de mélanger les deux méthodes d’impression: je veux utiliser cout
pour les impressions simples, et j’ai l’intention d’utiliser printf
pour produire d’énormes sorties (généralement en boucle). Je pense qu'il est prudent de le faire tant que je n'oublie pas de tirer la chasse 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 vos précieux commentaires. Récapitulatif des réponses: si vous voulez éviter des solutions compliquées, n'utilisez tout simplement pas endl
avec cout
car elle vide le tampon implicitement. Utilisez "\n"
à la place. Cela peut être intéressant si vous produisez large output.
La réponse directe est que oui, ça va.
Beaucoup de gens ont exprimé diverses idées sur la manière d'améliorer la vitesse, mais il semble y avoir un certain désaccord sur le point le plus efficace. J'ai décidé d'écrire un programme de test rapide pour avoir au moins une idée de la technique utilisée.
#include <iostream>
#include <string>
#include <sstream>
#include <time.h>
#include <iomanip>
#include <algorithm>
#include <iterator>
#include <stdio.h>
char fmt[] = "%s\n";
static const int count = 3000000;
static char const *const string = "This is a string.";
static std::string s = std::string(string) + "\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<count; i++)
printf(fmt, string);
}
void use_puts() {
for (int i=0; i<count; i++)
puts(string);
}
void use_cout() {
for (int i=0; i<count; i++)
std::cout << string << "\n";
}
void use_cout_unsync() {
std::cout.sync_with_stdio(false);
for (int i=0; i<count; i++)
std::cout << string << "\n";
std::cout.sync_with_stdio(true);
}
void use_stringstream() {
std::stringstream temp;
for (int i=0; i<count; i++)
temp << string << "\n";
std::cout << temp.str();
}
void use_endl() {
for (int i=0; i<count; i++)
std::cout << string << std::endl;
}
void use_fill_n() {
std::fill_n(std::ostream_iterator<char const *>(std::cout, "\n"), count, string);
}
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é sur Windows après avoir compilé avec VC++ 2013 (versions x86 et x64). La sortie d'une exécution (avec la sortie redirigée vers un fichier sur 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 stringstream: 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 il y a quelques points que j'ai trouvés intéressants:
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 équivalente à puts(foo);
et génère du code en conséquence (c.-à-d. Qu'il génère du code pour appeler puts
au lieu de printf
). Le déplacement de la chaîne de format dans un tableau global et sa transmission en tant que chaîne de format produisent une sortie identique, mais la force à être produite via printf
au lieu de puts
. Bien sûr, il est possible qu’ils optimisent un jour cela également, mais au moins pour l’instant (g ++ 5.1), un test avec g++ -O3 -S
confirme qu’il appelle réellement printf
(où le code précédent a été compilé pour un appel à puts
).
L'envoi de std::endl
au flux ajoute une newline
et purge le flux. L'invocation ultérieure de cout.flush()
est superflue. Si cela a été fait en chronométrant cout
contre printf
, vous ne compariez pas des pommes avec des pommes.
Par défaut, les flux de sortie standard C et C++ sont synchronisés, de sorte que l'écriture sur l'un entraîne un vidage de l'autre, de sorte que les vidages explicites ne sont pas nécessaires.
Notez également que le flux C++ est synchronisé avec le flux C.
Il est donc très difficile de rester synchronisé.
Une autre chose à noter est de vous assurer de vider les flux de la même quantité. Si vous videz continuellement le flux sur un système et pas sur l'autre, cela affectera définitivement la vitesse des tests.
Avant de supposer que l'un est plus rapide que l'autre, vous devriez:
Vous pouvez améliorer encore les performances de printf
en augmentant la taille de la mémoire 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 et le limiteur de performances les plus coûteux.
Bien sûr, si la sortie de cout
est mélangée à stdout
, les vidages de mémoire tampon annulent l’objectif 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 vos performances de sortie avec cout
.
Ne vous inquiétez pas des performances entre printf
et cout
. Si vous souhaitez améliorer les performances, séparez les sorties formatées des sorties non formatées.
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 texte formaté à partir de texte brut, vous pouvez réaliser des astuces telles que:
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 en chaîne, puis à utiliser la sortie 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 produisez de contenu, plus votre programme sera rapide. Un effet secondaire sera que votre taille d’exécutable sera également réduite.
Eh bien, je ne vois aucune raison d'utiliser le cout pour être honnête. C'est complètement fou d'avoir un énorme gabarit encombrant pour faire quelque chose d'aussi simple qui sera dans chaque fichier. En outre, c'est comme s'il était conçu pour être aussi lent que possible à taper et après la millionième fois de taper <<<< puis taper la valeur entre les deux et obtenir quelque chose de similaire> nomVariable >>> par accident, je ne veux plus jamais le refaire .
Sans compter que si vous incluez un espace de noms std, le monde finira par imploser, et si vous n'utilisez pas votre charge de frappe, elle devient encore plus ridicule.
Cependant, je n'aime pas beaucoup l'impression non plus. Pour moi, la solution consiste à créer mon propre cours concret, puis à appeler tout ce qui est nécessaire à l'intérieur. Ensuite, vous pouvez avoir un io vraiment simple de la manière que vous voulez et avec l’implémentation que vous voulez, le formatage que vous voulez, etc. (en général, vous voulez que les flottants soient toujours un moyen, par exemple, de ne pas les formater à 800 moyens sans aucune raison. en formatant à 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 ce que vous voulez. Avec beaucoup de fichiers, il est surprenant de constater combien cela améliore également le temps de compilation.
De plus, il n’ya rien de mal à mélanger C et C++, cela devrait être fait de manière juste et si vous utilisez les choses qui causent les problèmes avec l’utilisation de C en premier lieu, c’est sûr que le moindre de vos soucis est la difficulté à mélanger C et C++.
Le mélange de méthodes C++ et C a été recommandé par mes livres C++, FYI. Je suis à peu près sûr que les fonctions C piétinent l'état attendu/maintenu par C++.