j'ai un fichier du style :
label 2.3 4.5 5.6
label2 1.2 1.0 -2.
...
qui est assez gros (près d'1 Go).
J'avais un programme C à base de fscanf.
En passant de C (à base de fscanf) à c++ avec ifstream (voir le code en
bas), je passe de 1min 40s en C à 3min 10s en c++.
Je trouve que ca fait un gap quand même.
Comme je n'ai pas trop d'expérience sur les fichiers en c++, je m'en
remets à vous.
Vous pouvez me dire si vous voyez des choses à améliorer pour avoir de
meilleures performances (plus proches du C)?
Je joins un bout de code avec uniquement la lecture du fichier et
l'extraction des données.
Le code C fait la même chose mais avec fscanf et feof. Je peux le donner
si ca peut aider.
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char ** argv)
{
ifstream ifs("fichier.toto");
float x,y,z;
string lc;
int count =0;
int i;
while (! ifs.eof())
{
ifs >> lc>>x>>y>>z;
if (!ifs.eof())
{
count ++;
}
}
cout <<"count = "<<count<<endl;
return 0;
}
Merci d'avance.
Ploc.
Précision importante
- même machine, même charge
- gcc et g++ v4.1.2
- mêmes options d'optimisation -O2 (même si ca ne semble pas beaucoup
jouer ici).
>Le code C fait la même chose mais avec fscanf et feof. Je peux le donner
>si ca peut aider.
Ce serait probablement une bonne idée, histoire d'avoir une référence
pour la performance.
>En passant de C (à base de fscanf) à c++ avec ifstream (voir le code en
>bas), je passe de 1min 40s en C à 3min 10s en c++.
As-tu fait les mesures avec time (et, accessoirement, un top dans une
autre console) ? 10 Mo/s, ce n'est pas très loin de la vitesse
effective de lecture d'un fichier sur disque ; time (la fonction de
bash, pas celle de C ou C++) permet de savoir combien de temps le
processeur met effectivement pour décoder le fichier. Tu t'apercevras
peut-être que le programme en C++ est encore plus mauvais que tu le
croyais.
> while (! ifs.eof())
> {
> ifs >> lc>>x>>y>>z;
> if (!ifs.eof())
> {
> count ++;
> }
> }
Le code ci-dessus me paraît louche. D'une part, tu ne devrais pas être
obligé d'appeler eof() deux fois dans la boucle. D'autre part, tu n'as
aucun contrôle d'erreur.
Si tu as l'assurance absolue que le fichier est écrit dans un format
précis, et qu'aucune erreur ne peut s'y glisser (ou que, en cas
d'erreur, un plantage ou un comportement erratique sont acceptables),
tu peux certes zapper certains tests, mais il faut le dire
explicitement.
En particulier, l'assurance absolue que la longueur de chaque ligne
est inférieure à une valeur fixée à l'avance, est indispensable pour
lire une chaîne de caractères avec fscanf (ou scanf). Sinon, il faut
utiliser fgets + sscanf.
Si tu n'as pas besoin de la portabilité (ou si tu peux te permettre de
faire une version spéciale pour Windows), mmap() peut s'avérer une
piste à creuser pour améliorer les performances tout en réduisant les
problèmes.
Bien que j'utilise << avec plaisir, l'opérateur >> m'a toujours laissé
dubitatif. Du coup, généralement, je lis un fichier ligne par ligne
(avec std::getline()), puis j'analyse chaque ligne moi-même (y
compris, dans certains cas, avec sscanf). Mais je m'intéresse plus au
contrôle d'erreur qu'aux performances.
pas vraiment, avec fread et un buffer de 64Ko, je lis 180Mo en 3 sec.!
(à la première lecture d'un fichier non caché - les lectures suivantes
prennent 172 ms), écart mesuré avec _ftime sur un binaire (fopen("rb"))
le "temps de lecture effectif" a tout chance d'être négligeable par
rapport au traitement de texte (scanf ou istream) qui est le problème
ici.
> [...] puis j'analyse chaque ligne moi-même (y compris, dans
> certains cas, avec sscanf). Mais je m'intéresse plus au
> contrôle d'erreur qu'aux performances.
et tu as p.e. les 2.
Sylvain.
une lecture ne se fait pas sur std::ios::eof()
je verrais plutot la lecture comme ca (non testé)
while(( ifs >> x >> y >> z ))
{}
cf FAQ C++ de developpez
>pas vraiment, avec fread et un buffer de 64Ko, je lis 180Mo en 3 sec.!
Ça m'étonnerait beaucoup que tu aies une telle vitesse sur toute la
surface du disque.
pour être honnête j'étais sous les 3 sec. (2.6); je ne pense pas qu'un
fichier à la périphérie prendra 10 fois le temps d'un fichier près du
centre - ce faisant je n'imagine pas que l'on parle d'un disk de +5 ans
d'age.
je répète le point qui me semble "time consuming" et qui donc devrait
être imho le point d'intérêt : la conversion texte en float.
la lecture binaire est rapide - ok sauf en PATA de base avec un disk
poussif.
Sylvain.
2.8 sec. (pas .6) sur la 1ière partition, 5.1 sec. sur la 4ième
et dernière, ça influe ... d'un facteur <2 pas de 6.
Sylvain.
>En passant de C (à base de fscanf) à c++ avec ifstream (voir le code en
>bas), je passe de 1min 40s en C à 3min 10s en c++.
Après quelques essais, j'ai bien l'impression que parser soi-même les
données apporte un gain de performances assez confortable.
Soit donc un
struct Destination
{
std::string label;
float a, b, c;
};
que je remplis par lecture d'une ligne dans le fichier, puis que je
jette sans rien en faire. Recommencer jusqu'à épuisement du fichier.
En cas d'erreur de format, on considère que la ligne est déficiente,
et on passe à la suivante.
J'ai fait les tests sur un AMD Athlon64 double coeur, 2,4 GHz, avec
2 Go de RAM. Debian 64 bits, g++ 4.1.2, optimisation "-O3".
Commençons par un fichier de 953 Mo.
real 0m24.446s
user 0m11.129s
sys 0m0.412s
953.674 Mo une deuxième fois :
real 0m13.218s
user 0m11.277s
sys 0m0.372s
La deuxième fois, le fichier était déjà en cache, ce qui semble
confirmer que la ligne "user" représente bien le temps que le
processeur met à convertir les données.
Un fichier un peu plus gros (3814 Mo) confirme une vitesse de
traitement d'environ 85 Mo/s :
real 2m34.111s
user 0m45.239s
sys 0m3.844s
Si maintenant je mets en commentaire la ligne 134 (i.e. je supprime
l'appel à std::string::assign()), j'obtiens, pour mon fichier de
3814 Mo :
real 2m5.296s
user 0m17.713s
sys 0m3.844s
soit 2,5 fois moins. La recopie de la chaîne de caractères "labelXXX"
est donc, de loin, ce qui prend le plus de temps.
C'est bon à savoir : si jamais tu n'as besoin de ce texte que de temps
en temps, tu peux te contenter de conserver les pointeurs sur le début
et la fin de la chaîne (la fonction mmap() garantit que la mémoire
pointée sera toujours accessible). Et si ce texte est juste là pour
faire joli, et ne sert à rien, tu peux carrément ne pas conserver sa
valeur.
Avertissement : le code ci-dessous est avant tout un code de test,
conçu pour rechercher la méthode la plus rapide.
Même s'il m'a l'air de fonctionner correctement, et comporte du code
de détection d'erreur (sauf dans main()), il ne s'agit pas de code de
production.
#include <string>
class Source
{
public:
bool Fini() const { return ptr == fin; }
void Avancer() { ++ptr; }
char Get() const { return *ptr; }
char const* Ptr() const { return ptr; }
public:
Source (char const* debut, char const* fin_)
: ptr (debut), fin (fin_) {}
private:
char const* ptr;
char const* const fin;
};
struct Destination
{
std::string label;
float a, b, c;
};
void RechercheDebutLigne (Source& src)
{
bool fin_ligne_trouvee= false;
while (!src.Fini())
{
if (src.Get()=='\n')
{
fin_ligne_trouvee= true;
}
else if (fin_ligne_trouvee)
{
return;
}
src.Avancer();
}
}
bool Lire (float& dest, Source& src)
{
dest= 0;
bool negatif= false;
float decimale= 0.1;
bool info_rencontree= false;
bool virgule_rencontree= false;
while (!src.Fini())
{
if (src.Get() == '\n')
{
break;
}
if (src.Get() == '-')
{
if (info_rencontree)
{
return false; // mal formé
}
negatif= true;
}
else if (src.Get() == '.')
{
if (virgule_rencontree)
{
return false; // mal formé
}
virgule_rencontree= true;
info_rencontree= true;
}
else if (src.Get() >= '0' && src.Get() <= '9')
{
if (virgule_rencontree)
{
dest+= decimale * (src.Get()-'0');
decimale /= 10;
}
else
{
dest*= 10;
dest+= src.Get()-'0';
}
info_rencontree= true;
}
else if (info_rencontree)
{
break;
}
src.Avancer();
}
if (negatif)
{
dest= -dest;
}
return info_rencontree;
}
bool Lire (std::string& dest, Source& src)
{
char const* debut= 0;
while (!src.Fini())
{
if (src.Get() == '\n')
{
break;
}
if (src.Get() == ' ' || src.Get() == '\t')
{
if (debut != 0)
{
break;
}
}
else if (debut == 0)
{
debut= src.Ptr();
}
src.Avancer();
}
if (debut == 0)
{
return false;
}
else
{
dest.assign (debut, src.Ptr()-debut);
return true;
}
}
bool Lire (Destination& dest, Source& src)
{
bool ok= Lire (dest.label, src)
&& Lire (dest.a, src)
&& Lire (dest.b, src)
&& Lire (dest.c, src);
RechercheDebutLigne (src);
return ok;
}
#include <iostream>
#include <fcntl.h>
#include <assert.h>
#include <sys/mman.h>
int main()
{
using namespace std;
char const nom_src[]= "data.txt";
unsigned int const taille_src= 4000000000;
int fichier= open (nom_src, O_RDONLY);
void* v_data= mmap
(0, taille_src, PROT_READ, MAP_PRIVATE, fichier, 0);
assert (v_data!=MAP_FAILED);
char const* data= static_cast<char const*>(v_data);
Source src (data, data + taille_src);
for (int num_ligne=0; !src.Fini(); ++num_ligne)
{
Destination d;
if (!Lire (d, src))
{
//cerr << "Erreur ligne " << num_ligne << endl;
}
}
double taille_en_Mo= taille_src/1024.0/1024.0;
cerr << taille_en_Mo << " Mo" << endl;
}/* C'est la fin du programme : le fichier est automatiquement fermé ;
il n'y a pas eu d'écritures, donc on n'a pas à se soucier de savoir si
la fermeture a réussi. */
En fait, tu peux même remplacer std::string par une "chaîne du
pauvre", moins riche en fonctionnalités mais plus rapide dans ce cas
précis :
class StringDuPauvre
{
public:
void assign (char const* debut_, unsigned int longueur_)
{ debut= debut_, longueur= longueur_; }
typedef char const* const_iterator;
const_iterator begin() const { return debut; }
const_iterator end() const { return debut+longueur; }
size_t size() const { return longueur; }
private:
char const* debut;
unsigned int longueur;
};
std::ostream& operator << (std::ostream& os, StringDuPauvre const& s)
{
os.write (s.begin(), s.size());
return os;
}
typedef StringDuPauvre String;
//typedef std::string String;
struct Destination
{
String label;
float a, b, c;
};
(La durée de vie des tableaux pointés est gérée par mmap().)
Comme prévu, j'obtiens une vitesse de calcul deux fois plus élevée.
Les mesures ont été faites avec 'time ./mon_prog'. Et le temps donné est
le temps reel, et mesuré dans les mêmes conditions de charge et
d'ocupation mémoire.
>
>
>> while (! ifs.eof())
>> {
>> ifs >> lc>>x>>y>>z;
>> if (!ifs.eof())
>> {
>> count ++;
>> }
>> }
>
> Le code ci-dessus me paraît louche. D'une part, tu ne devrais pas être
> obligé d'appeler eof() deux fois dans la boucle. D'autre part, tu n'as
> aucun contrôle d'erreur.
>
> Si tu as l'assurance absolue que le fichier est écrit dans un format
> précis, et qu'aucune erreur ne peut s'y glisser (ou que, en cas
> d'erreur, un plantage ou un comportement erratique sont acceptables),
> tu peux certes zapper certains tests, mais il faut le dire
> explicitement.
> En particulier, l'assurance absolue que la longueur de chaque ligne
> est inférieure à une valeur fixée à l'avance, est indispensable pour
> lire une chaîne de caractères avec fscanf (ou scanf). Sinon, il faut
> utiliser fgets + sscanf.
>
Le bout de code que j'ai écrit est juste là pour des questions de test
que j'ai codé pour essayer d'expliquer les différences de rapidité entre
deux versions d'un programme réel. Mais la, ca n'a plus grand chose à
voir avec l'existant.
Ce n'est pas très génant pour nous de garder le code existant en C.
Mon intention ici est plus d'apprendre ce qui peut accélérer le
traitement de fichiers, ou au moins expliquer la différence de rapidité.
>
> Si tu n'as pas besoin de la portabilité (ou si tu peux te permettre de
> faire une version spéciale pour Windows), mmap() peut s'avérer une
> piste à creuser pour améliorer les performances tout en réduisant les
> problèmes.
>
Malheureusement, ca doit compiler (de préférence la même version) avec
visual studio.
>
> Bien que j'utilise << avec plaisir, l'opérateur >> m'a toujours laissé
> dubitatif. Du coup, généralement, je lis un fichier ligne par ligne
> (avec std::getline()), puis j'analyse chaque ligne moi-même (y
> compris, dans certains cas, avec sscanf). Mais je m'intéresse plus au
> contrôle d'erreur qu'aux performances.
Le controle est important, mais si les perfs ne suivent pas, on ne va
probablement pas changer le code existant.
Ploc.
Le voici.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv)
{
FILE *f=fopen("fichier.toto", "r");
float x,y,z;
char *lc=(char*)malloc(100 * sizeof(lc));
int count =0;
while (! feof(f))
{
fscanf(f, "%s %g %g %g", lc, &x, &y, &z);
if (! feof(f))
{
count ++;
}
}
fclose(f);
printf("count = %i\n", count);
return 0;
}
C'est un peu mieux (de l'ordre de 4s).
> char *lc=(char*)malloc(100 * sizeof(lc));
>[...]
> fscanf(f, "%s %g %g %g", lc, &x, &y, &z);
Comme indiqué précédemment, si jamais la longueur de la chaîne
"labelXXX" est supérieure à 100, boum !
fscanf_s(f, "%s %g %g %g", lc, 100, &x, &y, &z);
est supporté par gcc, non ?
Sylvain.
Non.
gcc est un compilateur.
La bibliotheque standard (et ses extensions) n'en font pas partie.
>est supporté par gcc, non ?
Mais du coup, ce n'est plus du C. Et si j'ai bien saisi, l'OP a besoin
de la portabilité.
(Par ailleurs, même si on accepte ce code, il faudra refaire le test
de performance.)
>Le bout de code que j'ai écrit est juste là pour des questions de test
>que j'ai codé pour essayer d'expliquer les différences de rapidité entre
>deux versions d'un programme réel. Mais la, ca n'a plus grand chose à
>voir avec l'existant.
Ben justement, le code en C et le code en C++ ne font pas la même
chose. Difficile donc de les comparer.
>Ce n'est pas très génant pour nous de garder le code existant en C.
Si le code existant fonctionne bien (ce qui n'est pas le cas du code C
que tu as fourni ici), effectivement, le changer ne sert pas à
grand-chose.
Ça se confirme. Et je me suis aperçu que mmap() ne change pas
grand-chose.
Le code que j'avais proposé, avec une fonction main() plus standard
(cf ci-dessous), donne d'aussi bons résultats.
Manifestement, au moins dans gcc, scanf est effroyablement lent, et >>
encore plus.
Seule autre modification : dans la série "cachez ce char const* que je
ne saurais voir", j'ai uniformisé pour n'utiliser que std::string,
même dans Source.
Voici donc un programme en C++ standard, certes un peu long, mais qui
est très largement plus rapide que ton code en C (et, a fortiori, que
ton code en C++).
#include <string>
class Source
{
public:
typedef std::string::const_iterator const_iterator;
bool Fini() const { return ptr == Fin(); }
void Avancer() { ++ptr; }
char Get() const { return *ptr; }
const_iterator Ptr() const { return ptr; }
const_iterator Fin() const { return src.end(); }
Source (std::string const& src_) : src (src_), ptr (src_.begin())
{}
private:
std::string const& src;
const_iterator ptr;
};
Source::const_iterator debut= src.Fin();
while (!src.Fini())
{
if (src.Get() == '\n')
{
break;
}
if (src.Get() == ' ' || src.Get() == '\t')
{
if (debut != src.Fin())
{
break;
}
}
else if (debut == src.Fin())
{
debut= src.Ptr();
}
src.Avancer();
}
if (debut == src.Fin())
{
return false;
}
else
{
dest.assign (debut, src.Ptr());
return true;
}
}
bool Lire (Destination& dest, Source& src)
{
bool ok= Lire (dest.label, src)
&& Lire (dest.a, src)
&& Lire (dest.b, src)
&& Lire (dest.c, src);
RechercheDebutLigne (src);
return ok;
}
#include <iostream>
#include <fstream>
int main()
{
using namespace std;
char const nom_src[]= "data.txt";
ifstream ifs (nom_src);
string ligne;
unsigned int num_ligne= 0;
while (std::getline (ifs, ligne, '\n'))
{
++num_ligne;
Destination d;
Source s (ligne);
if (!Lire (d, s))
{
// erreur
}
}
cerr << num_ligne << " lignes lues" << endl;
}
qui, quoi ?
> Et si j'ai bien saisi, l'OP a besoin de la portabilité.
ces _s sont supportés par VC 14+ (second compilo listé par le PO)
de nombreux posts (web) sur le net laissaient penser que gcc
(et ses libraries usuelles !...) les supportaient aussi.
> si on accepte ce code, il faudra refaire le test de performance.
dans l'absolu oui, je commentais seulement le rique de BO.
(ça n'impliquait pas que ce serait a priori moins ou plus rapide).
Sylvain.
J'ai finalement pu tester ce programme.
Tel quel il est assez proche en durée de la version c.
En sortant la ligne 'Destination d' de la boucle, on fait même mieux (on
teste toujours le temps d'I/O, pour l'utilisation des valeurs extraites,
on verra plus tard).
Par contre, le mmap passera jamais sous visual studio.
La remarque sur l'extraction des labels est très interressante.
Malheureusement, j'en ai besion assez vite. Dommage.
>Tel quel il est assez proche en durée de la version c.
Bizarre. J'ai une différence d'un facteur 4 ou 5.
(Sauf en l'absence d'optimisation ("-Ox"))
>Par contre, le mmap passera jamais sous visual studio.
Il y a l'équivalent presque exact sous Windows.
De toute façon je t'ai donné une autre version, en C++ standard.
>La remarque sur l'extraction des labels est très interressante.
Qu'est-ce que tu dois en faire exactement, de ces labels ?
Malheureusement, le visual studio qu'on nous impose est loin d'etre
aussi récent...
C'est effectivement très rapide : de l'ordre d' 1min 6s chez moi (37.7
Millions de lignes, 1.3Go) contre 1min 35s dans la version c.
J'aurais cru que les ifstream seraient plus rapides.
En tout cas, je pense que si on décide de passer cette partie en c++, on
parsera le fichier 'à la main'.
Merci pour les conseils.
Ploc.
Dans le format original qui est un peu plus complexe, les lignes peuvent
contenir des références à d'autres labels définis en amont pour
construire une structure d'arbre.
>C'est effectivement très rapide : de l'ordre d' 1min 6s chez moi (37.7
>Millions de lignes, 1.3Go) contre 1min 35s dans la version c.
Quel type de machine as-tu, et quelles options de compilation
utilises-tu ?
J'obtiens un temps comparable pour ta version en C (79 secondes), mais
un temps bien plus réduit pour ma version (24 secondes).
Par contre, sans optimisation, mon programme est impressionnant de
nullité : 2 minutes 40 secondes !
Le fait que l'optimisation change considérablement le temps
d'exécution est d'ailleurs compréhensible, puisque le code fait tout
le boulot, quasiment sans appel de fonctions externes.
cl.exe version 14 n'est "que" VS 2005, cela devait être présent dans
la version 2003 également, les versions express (gratuites) peuvent
gérer certains cas, mais qu'importe puisque _s est non conforme.
Sylvain.
Athlon XP 2500+/ 1Go RAM + DD un peu poussif (debit à 40Mo/s d'apres
hdparm -t).
Pour la compilation, c'est tout du -O2.
Il est clair que dans mon cas, ma machine de dev bloque au niveau du
debit disque + memoire. Mais je le sais et j'attendais des perfs
comparables au C, sans forcément les éclater comme c'est le cas ici.
Sans optimisation, j'obtiens >= 3min 40s.
>Athlon XP 2500+/ 1Go RAM + DD un peu poussif (debit à 40Mo/s d'apres
>hdparm -t).
J'ai 2 Go, ce qui explique que le fichier tienne en cache chez moi et
pas chez toi. Mais...
>Il est clair que dans mon cas, ma machine de dev bloque au niveau du
>debit disque + memoire. Mais je le sais et j'attendais des perfs
>comparables au C,
...justement, time affiche (sur sa deuxième ligne) le temps processeur
réellement utilisé par le programme, ce qui, en cas d'E/S lentes, est
très inférieur à la durée entre le début et la fin de l'exécution.
> label 2.3 4.5 5.6
> label2 1.2 1.0 -2.
> ...
> qui est assez gros (près d'1 Go).
> J'avais un programme C à base de fscanf. En passant de C (à
> base de fscanf) à c++ avec ifstream (voir le code en bas), je
> passe de 1min 40s en C à 3min 10s en c++. Je trouve que ca
> fait un gap quand même. Comme je n'ai pas trop d'expérience
> sur les fichiers en c++, je m'en remets à vous.
> Vous pouvez me dire si vous voyez des choses à améliorer pour
> avoir de meilleures performances (plus proches du C)?
> Je joins un bout de code avec uniquement la lecture du fichier
> et l'extraction des données. Le code C fait la même chose
> mais avec fscanf et feof.
Je doute que la reste du code est identique, parce que....
> Je peux le donner si ca peut aider.
> #include <iostream>
> #include <fstream>
> using namespace std;
> int main(int argc, char ** argv)
> {
> ifstream ifs("fichier.toto");
> float x,y,z;
> string lc;
Tu ne peux pas lire un std::string avec fscanf. Il y a donc des
allocations dynamiques (probablement) ici que tu n'as pas dans
l'autre. Ce qui peut faire une différence.
(Aussi, en passant, tu n'as pas inclu <string>. L'utilisation de
std::string est donc un comportement indéfini. Mais ce n'est
certainement pas la raison de ton problème de performance.)
> int count =0;
> int i;
> while (! ifs.eof())
> {
> ifs >> lc>>x>>y>>z;
Aussi : pourquoi pas simplement :
while ( ifs >> lc >> x >> y >> z ) ...
C'est nettement plus idiomatique.
> if (!ifs.eof())
> {
> count ++;
> }
> }
> cout <<"count = "<<count<<endl;
> return 0;
> }
Selon les implémentations, il peut y avoir plus ou moins de
différences de performances entre iostream et FILE*. En général,
en faveur des FILE* (mais ce n'est pas forcement universel). Une
partie des différences, en revanche, c'est qu'on a une tendance
à utiliser des idiomes plus sûrs avec iostream, de lire dans un
std::string, par exemple, plutôt que dans un char[].
--
James Kanze (GABI Software) email:james...@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
>Selon les implémentations, il peut y avoir plus ou moins de
>différences de performances entre iostream et FILE*. En général,
>en faveur des FILE* (mais ce n'est pas forcement universel).
Ce que je ne comprends pas, c'est pourquoi mon implémentation
(bricolée en quelques minutes) est très largement plus rapide que
"operator >> (istream&, double&)".
D'autant que je commence par lire une ligne dans un string avec
getline(), puis je parse cette chaîne (en faisant les vérifications
qui vont bien), alors que operator>> ne passe pas par cette étape
intermédiaire.
Comme je ne pense pas être meilleur que les auteurs de g++ dans leur
propre domaine, je me doute qu'il y a une explication quelque part,
mais je ne vois pas où. Et on ne parle pas ici de quelques % de
différence, mais bien d'un facteur 4 (grosso modo).
Peut être l'indirection causée par le codecvt. Ton code ne prends pas en
compte la localisation bien que ça n'explique pas un facteur 4.
> Comme je ne pense pas être meilleur que les auteurs de g++ dans leur
> propre domaine, je me doute qu'il y a une explication quelque part,
> mais je ne vois pas où. Et on ne parle pas ici de quelques % de
> différence, mais bien d'un facteur 4 (grosso modo).
Ou peut être il n'y a pas eu d'effort de la part des auteurs de g++ pour
optimiser iostream.
D'après B.Stroustrup, iostream est plus rapide (en théorie)
http://www.research.att.com/~bs/new_learning.pdf.
Michael
Est-ce que tu es sûr que ton implémentation est 100% correcte,
dans tous les cas ? Entre les options de formattage et les
problèmes d'arrondi (voir "How to read floating point numbers
accurately", de William Clinger,
http://portal.acm.org/citation.cfm?id=93557), il y a pas mal de
choses qui peuvent rendre une implémentation « correcte »
nettement moins rapide qu'une à peu près correcte. (Évidemment,
l'arithmétique entière à 64 bits, ce qui est universalement
disponible aujourd'hui, simplifie déjà beaucoup.)
> > On Mon, 5 May 2008 02:42:08 -0700 (PDT), James Kanze
> > <james.ka...@gmail.com>:
> >> Selon les implémentations, il peut y avoir plus ou moins de
> >> différences de performances entre iostream et FILE*. En général,
> >> en faveur des FILE* (mais ce n'est pas forcement universel).
> > Ce que je ne comprends pas, c'est pourquoi mon
> > implémentation (bricolée en quelques minutes) est très
> > largement plus rapide que "operator >> (istream&, double&)".
> > D'autant que je commence par lire une ligne dans un string
> > avec getline(), puis je parse cette chaîne (en faisant les
> > vérifications qui vont bien), alors que operator>> ne passe
> > pas par cette étape intermédiaire.
> Peut être l'indirection causée par le codecvt. Ton code ne
> prends pas en compte la localisation bien que ça n'explique
> pas un facteur 4.
La localisation coûte cher dans std::filebuf. Ailleurs, ça
dépend; d'après ce qu'on m'a dit, c'est possible d'en minimiser
le coût. (Au moins une personne m'a dit la même chose pour
std::filebuf. Mais il avoue que c'est assez compliqué, et que
les implémentations aujourd'hui ne le font pas.)
> > Comme je ne pense pas être meilleur que les auteurs de g++
> > dans leur propre domaine, je me doute qu'il y a une
> > explication quelque part, mais je ne vois pas où. Et on ne
> > parle pas ici de quelques % de différence, mais bien d'un
> > facteur 4 (grosso modo).
> Ou peut être il n'y a pas eu d'effort de la part des auteurs
> de g++ pour optimiser iostream.
> D'après B.Stroustrup, iostream est plus rapide (en
> théorie)http://www.research.att.com/~bs/new_learning.pdf.
Attention. La date sur cet article est 1999. Ce qui fait penser
qu'il parle des iostream classique, et non des iostream
standard. La gestion de la facette codecvt dans filebuf rend
l'optimisation nettement plus difficile.
>Est-ce que tu es sūr que ton implémentation est 100% correcte,
>dans tous les cas ?
Effectivement, c'est probablement lą l'explication.
Il y a eu un débat récemment là dessus sur les ML C++, sans pour
autant qu'un consensus clair se dégage à mon avis.
Je serais curieux de voir ce que donne le programme par exemple avec
les fastreams : http://www.msobczak.com/prog/fastreams/