Différence de Performance entre C et c++ Style fichier IO
J'ai toujours entendu dire que les opérations d'E/S de fichiers c++ sont beaucoup plus lentes que les e/s de style C mais je n'ai pas trouvé de références pratiques sur la façon dont elles sont réellement lentes, alors j'ai décidé de le tester sur ma machine (Ubuntu 12.04, GCC 4.6.3, format de partition ext4).
D'abord j'ai écrit un fichier ~ 900MB dans le disque.
C++ (ofstream
): 163s
ofstream file("test.txt");
for(register int i = 0; i < 100000000; i++)
file << i << endl;
C (fprintf
): 12s
FILE *fp = fopen("test.txt", "w");
for(register int i = 0; i < 100000000; i++)
fprintf(fp, "%dn", i);
Je m'attendais à une telle sortie, cela montre que l'écriture dans un fichier est beaucoup plus lente en C++ le C. Ensuite, j'ai lu le même fichier en utilisant C et c++ I / O. ce qui m'a fait m'exclamé qu'il n'y a presque aucune différence de performance lors de la lecture du fichier.
C++ (ifstream
): 12s
int n;
ifstream file("test.txt");
for(register int i = 0; i < 100000000; i++)
file >> n;
C (fscanf
): 12s
FILE *fp = fopen("test.txt", "r");
for(register int i = 0; i < 100000000; i++)
fscanf(fp, "%d", &n);
Alors, pourquoi prend - il si longtemps à exécuter l'écriture en utilisant stream? Ou, Pourquoi lire en utilisant stream est si rapide par rapport à l'écriture?
Conclusion:, Le coupable est le std::endl
, que les réponses et les commentaires l'ont souligné. La modification de la ligne
file << i << endl;
de
file << i << 'n';
a réduit le temps de fonctionnement à 16s de 163s.
3 réponses
Vous utilisez endl
pour imprimer une nouvelle ligne. C'est le problème ici, car il fait Plus que simplement imprimer une nouvelle ligne - endl
aussi vide le tampon qui est une opération coûteuse (si vous le faites à chaque itération).
Utilisez \n
Si vous voulez dire:
file << i << '\n';
Et aussi, doit compiler votre code en mode release (c'est-à-dire activer les optimisations).
Non, l'entrée/sortie c++ n'est pas sensiblement plus lente que C-si quoi que ce soit, une implémentation moderne devrait être légèrement plus rapide sur l'entrée/sortie formatée car elle n'a pas besoin d'analyser une chaîne de format, et le formatage est déterminé au moment de la compilation via le chaînage des opérateurs de flux.
Voici quelques mises en garde à considérer dans un indice:
- compilez avec des optimisations complètes (
-O3
) pour obtenir une comparaison équitable. - un benchmark approprié doit estimer les biais – en pratique, cela signifie que vous devez répéter vos tests et les entrelacer. Pour le moment, votre code n'est pas robuste aux perturbations des processus d'arrière-plan. Vous devez également signaler une statistique sommaire des séries répétées pour détecter les valeurs aberrantes qui faussent les estimations.
- désactiver la synchronisation des flux C++ avec les flux C(
std::ios_base::sync_with_stdio(false);
) - Utilisez
'\n'
au lieu de (flushing)std::endl
- N'utilisez pas les déclarations
register
- cela ne fait simplement aucune différence et moderne les compilateurs l'ignorent probablement de toute façon.
Lorsque vous travaillez avec des fichiers volumineux avec fstream
, Assurez-vous de définir un tampon de flux >0.
Contre-intuitivement, la désactivation de la mise en mémoire tampon de flux réduit considérablement les performances. Au moins L'implémentation MSVC 2015 copie 1 caractère à la fois {[10] } vers filebuf
lorsqu'aucun tampon n'a été défini (voir streambuf::xsputn
), ce qui peut rendre votre application liée au processeur, ce qui entraînera des taux d'E/S inférieurs.
const size_t bufsize = 256*1024;
char buf[bufsize];
mystream.rdbuf()->pubsetbuf(buf, bufsize);
Vous pouvez trouver un exemple complet d'application ici.