62 votes

Comment obtenir IOStream à faire mieux?

La plupart auparavant C-utilisateurs préfèrent utiliser l' printf / scanf famille de fonctions en C++.

Même si j'avoue que je trouve l'interface bien mieux (surtout POSIX format de type et localisation), il semble que la très grande préoccupation est la performance.

Au coup d'oeil à sa question:

Comment puis-je accélérer, ligne par ligne, la lecture d'un fichier

Il semble que la meilleure réponse est d'utiliser fscanf... et que le C++ ifstream est systématiquement x2-x3 fois plus lent.

J'ai pensé que ce serait formidable si nous pouvions compiler un référentiel de "trucs" pour améliorer IOStreams de la performance: quels sont les travaux, ce qui ne fonctionne pas.

Côté de la réflexion sont les suivants:

  • mise en mémoire tampon (rdbuf()->pubsetbuf(buffer, size) ?)
  • synchronisation (std::ios_base::sync_with_stdio ?)
  • paramètres régionaux de manutention (Pourrait-on utiliser un coupé vers le bas locale ? Supprimer purement et simplement ?)

Et bien sûr, d'autres approches sont les bienvenus.

Remarque: un "nouveau" de la mise en œuvre, par Dietmar Kuhl, a été mentionné, mais j'ai été incapable de retrouver le maximum de détails à ce sujet, les références précédentes, semblent être un lien mort

47voto

Matthieu M. Points 101624

Voici ce que j'ai recueillies jusqu'à présent:

Mise en mémoire tampon:

Si par défaut la mémoire tampon est très faible, l'augmentation de la taille de la mémoire tampon peut certainement améliorer les performances:

  • il réduit le nombre de HDD hits
  • il réduit le nombre d'appels système

Tampon peut être définie par l'accès au sous-jacents streambuf mise en œuvre.

char Buffer[N];

std::ifstream file("file.txt");

file.rdbuf()->pubsetbuf(Buffer, N);
// the pointer reader by rdbuf is guaranteed
// to be non-null after successful constructor

Paramètres Régionaux De La Manipulation:

Paramètres régionaux pouvez effectuer la conversion de caractères, de filtrage, et plus de trucs astucieux où des nombres ou des dates sont impliqués. Ils passent par un système complexe de répartition dynamique et les appels virtuels, de sorte que leur suppression peut aider à coupe vers le bas la peine de frapper.

La valeur par défaut C locale n'est pas destiné à effectuer la conversion ainsi que d'être uniforme sur l'ensemble des machines. C'est une bonne valeur par défaut à utiliser.

Synchronisation:

Je ne pouvais pas voir tout l'amélioration de la performance à l'aide de cette installation.

On peut accéder à un mondial de réglage (membre statique d' std::ios_base) à l'aide de l' sync_with_stdio fonction statique.

Mesures:

Jouer avec cela, j'ai joué avec un programme simple, compilé à l'aide de gcc 3.4.2 sur SUSE 10p3 avec -O2.

C : 7.76532 e+06
C++: 1.0874 e+07

Ce qui représente un ralentissement d'environ 20%... pour le code par défaut. En effet, la falsification de la mémoire tampon (en C ou C++) ou les paramètres de synchronisation (C++) n'a pas produit d'amélioration.

Résultats par d'autres:

@Irfy sur g++ 4.7.2-2ubuntu1, -O3, virtualisé Ubuntu 11.10, 3.5.0-25-generic, x86_64, assez de ram/cpu, 196MB de plusieurs "trouver / >> largefile.txt" s'exécute

C : 634572 C++: 473222

C++ 25% plus rapide

@Matteo Italia sur g++ 4.4.5, -O3, Linux Ubuntu 10.10 x86_64 avec un nombre aléatoire de 180 MO fichier

C : 910390
C++: 776016

C++ 17% plus rapide

@Bogatyr sur g++ i686-apple-darwin10-g++-4.2.1 (GCC) 4.2.1 (Apple Inc. construire 5664), mac mini, 4 go de ram, de repos, sauf pour ce test avec une 168MB datafile

C : 4.34151 e+06
C++: 9.14476 e+06

C++ 111% plus lent

Donc la réponse est: c'est une qualité de la mise en œuvre question, et dépend vraiment de la plate-forme :/

Le code dans son intégralité ici pour ceux qui s'intéressent à l'analyse comparative:

#include <fstream>
#include <iostream>
#include <iomanip>

#include <cmath>
#include <cstdio>

#include <sys/time.h>

template <typename Func>
double benchmark(Func f, size_t iterations)
{
  f();

  timeval a, b;
  gettimeofday(&a, 0);
  for (; iterations --> 0;)
  {
    f();
  }
  gettimeofday(&b, 0);
  return (b.tv_sec * (unsigned int)1e6 + b.tv_usec) -
         (a.tv_sec * (unsigned int)1e6 + a.tv_usec);
}


struct CRead
{
  CRead(char const* filename): _filename(filename) {}

  void operator()() {
    FILE* file = fopen(_filename, "r");

    int count = 0;
    while ( fscanf(file,"%s", _buffer) == 1 ) { ++count; }

    fclose(file);
  }

  char const* _filename;
  char _buffer[1024];
};

struct CppRead
{
  CppRead(char const* filename): _filename(filename), _buffer() {}

  enum { BufferSize = 16184 };

  void operator()() {
    std::ifstream file(_filename, std::ifstream::in);

    // comment to remove extended buffer
    file.rdbuf()->pubsetbuf(_buffer, BufferSize);

    int count = 0;
    std::string s;
    while ( file >> s ) { ++count; }
  }

  char const* _filename;
  char _buffer[BufferSize];
};


int main(int argc, char* argv[])
{
  size_t iterations = 1;
  if (argc > 1) { iterations = atoi(argv[1]); }

  char const* oldLocale = setlocale(LC_ALL,"C");
  if (strcmp(oldLocale, "C") != 0) {
    std::cout << "Replaced old locale '" << oldLocale << "' by 'C'\n";
  }

  char const* filename = "largefile.txt";

  CRead cread(filename);
  CppRead cppread(filename);

  // comment to use the default setting
  bool oldSyncSetting = std::ios_base::sync_with_stdio(false);

  double ctime = benchmark(cread, iterations);
  double cpptime = benchmark(cppread, iterations);

  // comment if oldSyncSetting's declaration is commented
  std::ios_base::sync_with_stdio(oldSyncSetting);

  std::cout << "C  : " << ctime << "\n"
               "C++: " << cpptime << "\n";

  return 0;
}

1voto

CashCow Points 18388

Intéressant vous dire C programmeurs préfèrent printf lors de l'écriture de C++ que je vois beaucoup de code est C, autres que l'aide d' cout et iostream d'écrire la sortie.

Peuvent souvent obtenir de meilleures performances en utilisant filebuf directement (Scott Meyers mentionné dans Efficace STL), mais il y a relativement peu de documentation à l'aide de filebuf direct et la plupart des développeurs préfèrent std::getline ce qui est plus simple la plupart du temps.

En ce qui concerne les paramètres régionaux, si vous créez facettes vous obtiendrez souvent une meilleure performance par la création d'un jeu de paramètres régionaux fois avec toutes vos facettes, en gardant à l'stockées, et imprégnant dans chaque flux que vous utilisez.

Je ne vois d'un autre sujet sur ce ici récemment, donc c'est près d'être un doublon.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X