IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

 C++ Discussion :

Optimisation d'un code (lecture intensive de fichier binaire)


Sujet :

C++

  1. #1
    Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 5
    Points : 2
    Points
    2
    Par défaut Optimisation d'un code (lecture intensive de fichier binaire)
    Bonjour,

    J’aimerais avoir votre avis pour optimiser le temps de calcul d’un code.

    Pour résumer grossièrement mon code :
    • J’ai une classe qui contient une méthode de calcul

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Class object {
    public:
        object(QString objectName = "Not set");
        double computeValue(const double param1, const double param2) {return lectureFichier(param1, param2);}
    }
    • Mon main

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for(unsigned long long i=0 ; i<NOMBRE ; i++)
    	object->computeValue(valeur1, valeur2);
    avec NOMBRE qui peut être très très élevé (typiquement plusieurs millions) et valeur1 et valeur2 issus d’un calcul non « prévisible » (combinaison d’aléas).

    • Une bibliothèque de fonctions :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    // Le .h
    #ifndef BIBLIFUNCTIONS_H
    #define BIBLIFUNCTIONS _H
    #include <iostream>
    #include <fstream>
     
    double lectureFichier(const double param1, const double param2);
     
    #endif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    // Le .cpp
    #include "bibliFunctions.h"
     
        double lectureFichier(const double param1, const double param2) {
            ifstream file(PATH_FILE, ios::in | ios::binary);
    	Index = FUNCTION(param1, param2);
            file.seekg(index*SIZE_OF_DOUBLE);
            file.read(reinterpret_cast<char *>(&values), 4*SIZE_OF_DOUBLE);
            file.close();
     
    	return FONCTION(values) ; // Calcule 1 valeur en utilisant les 4 valeurs lues dans le fichier
    }
    Le fichier est une série de 260000 doubles (implicitement organisés en un tableau de 720 par 360). C’est un binaire de 2Mo. Ces fonctions se trouvent dans une bibliothèque à part car elles sont appelées à plusieurs endroits. Je précise également que je ne fais que lire le fichier (je ne fais jamais d’écriture).

    Mon problème est que j’ouvre et ferme de très nombreuses fois le ficher, ce qui me pénalise en temps de calcul.

    J’ai d’abord pensé à faire ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    // Le .h
    #ifndef BIBLIFUNCTIONS_H
    #define BIBLIFUNCTIONS _H
    #include <iostream>
    #include <fstream>
     
    static std::ifstream file(PATH_FILE, std::ios::in | std::ios::binary);
     
    double lectureFichier(const double param1, const double param2);
     
    #endif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    // Le .cpp
    #include "bibliFunctions.h"
     
        double lectureFichier(const double param1, const double param2) {
    	Index = FUNCTION(param1, param2);
            file.seekg(index*SIZE_OF_DOUBLE, ios::beg);
            file.read(reinterpret_cast<char *>(&values), SIZE_OF_DOUBLE);
    	return value ;
    }
    C’est bien plus rapide (je n’ouvre le fichier plus qu’une fois) mais je ne sais pas comment faire pour appeler le file.close().

    Je pensais aussi stocker le fichier (2Mo) en mémoire pour y accéder encore plus vite (pour éviter de lire sur le disque dur). Cependant, je ne sais pas trop sous quel format/quelles méthodes je dois utiliser car c’est un fichier binaire :
    • un istream avec les fonctions seekg et read ?
    • un stringsteam (mais il faut convertir les string en double, c’est peut-être pas le plus rapide) ?
    • autre format plus performant que je ne connais pas ?

    Donc j’aimerais un petit conseil pour optimiser au mieux ce code. Merci d’avance !

  2. #2
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    C'est le moment de faire intervenir une classe RAII, voire de se souvenir que fstream en est une.

    imagine une classe DataHolder ainsi faite:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class DataHolder {
    private:
    	ifstream & stream;
    public:
    	DataHolder(ifstream& source) : stream(source) {}
     
    	double computeValue(const double param1, const double param2) {
    		Index = FUNCTION(param1, param2);
    	        file.seekg(index*SIZE_OF_DOUBLE);
            	file.read(reinterpret_cast<char *>(&values), 4*SIZE_OF_DOUBLE);
    		return FONCTION(values)
    	}
    };
    Du coup, le main crée le stream, le donne à un DataHolder, et boucle sur celui-ci.
    En étant plus attentionné, je m'aperçois que Object pourrait fort bien contenir ce stream, ou en avoir une référence.


    Autre solution, changer la fonction pour lui donner la référence au stream.
    le main deviendrai:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    ifstream & file(...);
    for(unsigned long long i=0 ; i<NOMBRE ; i++)
    	object->computeValue(file, valeur1, valeur2);
    PS: j'espère que le nom Object est seulement pour l'exemple, et que ton code n'est pas basé sur un GodObject
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  3. #3
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 115
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 36
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 115
    Points : 32 967
    Points
    32 967
    Billets dans le blog
    4
    Par défaut
    Pourquoi tu rajouterais pas juste le fichier en question en paramètre de la méthode de lecture ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    // ouvrir fichier
    // deadly loop
    {// calcul}
    // fermer fichier
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  4. #4
    Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 5
    Points : 2
    Points
    2
    Par défaut
    Bonjour, merci pour vos réponses.

    Leternel, j'ai plusieurs questions :

    Dans la solution 1:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    class DataHolder {
    private:
    	ifstream & stream;
    public:
    	DataHolder(ifstream& source) : stream(source) {}
     
    	double computeValue(const double param1, const double param2) {
    		Index = FUNCTION(param1, param2);
    	        file.seekg(index*SIZE_OF_DOUBLE);
            	file.read(reinterpret_cast<char *>(&values), 4*SIZE_OF_DOUBLE);
    		return FONCTION(values)
    	}
    };
    Est-ce que ce ne devrait pas être stream.seekg() et stream.read() au lieu de file.seekg() et file.read() ?

    J'ai cru comprendre que ifstream crée un "pointer" vers le fichier ? Est-ce que je dois comprendre de tes propositions que créer un ifstream est aussi efficace que de stocker le fichier en mémoire ?

    Pour la solution 2, j'aurai au final une trentaine de fichiers qui seront tous utilisés par toutes mes classes donc la solution 1 me semble plus adaptée.

    Oui, "object" est un nom que j'ai donné pour l'exemple.


    Bousk,

    ton approche me demande de réorganiser pas mal de choses dans ce programme.

  5. #5
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Effectivement, c'est un copié collé malencontreux.
    fstream est un peu mieux qu'un fichier, mais gère ça proprement.

    créer un ifstream, c'est exactement ce que tu faisais.
    Ma solution permet de garder cette variable le temps nécessaire.

    La solution de Boosk revient sensiblement au même, sauf qu'il se passe de créer un Bidule™ pour garder la variable.
    C'est encore meilleur.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  6. #6
    Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 5
    Points : 2
    Points
    2
    Par défaut
    Ok. Merci pour toutes ces réponses !

  7. #7
    Membre confirmé
    Profil pro
    Consultant en technologies
    Inscrit en
    Octobre 2013
    Messages
    158
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations professionnelles :
    Activité : Consultant en technologies

    Informations forums :
    Inscription : Octobre 2013
    Messages : 158
    Points : 555
    Points
    555
    Par défaut
    Un fichier de 2MB sur une machine ordinaire avec des Gigas de RAM, ca vaut le coup de le stocker en RAM

    - Soit tu écrit un parser pour ton fichier binaire qui te le ré-écrit comme une suite d'objets/structures, c'est un peu de travail, mais ca te permet d'avoir quelques-chose bien propre

    - Soit tu sais exactement où tu dois tapper dans le fichier, tu n'as pas peur de compter les bits (ce que tu fais déjà à coup de seek) bref que tu es un bad boy of programming dans ce cas là tu te contente de mettre ton fichier dans un vector<char> (en fait un vectorr<int8_t> histoire d'être sur de la taille) voire (because STL containers are for pussies :p ) un tableau dynamque à l'ancienne (a tes risques et périls)

  8. #8
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par _zzyx_ Voir le message
    un vector<char> (en fait un vectorr<int8_t> histoire d'être sur de la taille)
    Si int8_t existe, alors char est sur 8 bits, c'est garanti par la norme.
    (Donc il suffit de vérifier, avec un static_assert par exemple, que char_BIT == 8 ; cela peut avoir un peu plus de sens selon le traitement à faire.)

    Sinon, je rejoins _zzyx_ pour conseiller de charger le fichier 2 MB en mémoire dans un tableau 2D de double (d'après le premier message). C'est plus simple et plus rapide si tu fais beaucoup de lectures / écritures.

  9. #9
    Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 5
    Points : 2
    Points
    2
    Par défaut
    Merci _zzyx_, Ehonn. Vous me conseillez donc un vector<double> ? J'ai encore une dernière question !

    Je lis 4 doubles consécutifs. Je découvre un peu les flux (j'ai un peu de mal à tout comprendre) et j'ai vu istringsteam (j'ai l'impression que ça permet de stocker un flux complet) et il y a aussi les fonctions seekg et read ? Est-ce que ça pourrait être une possibilité. Si oui, quelles solution serait la plus rapide (istringstream ou vector<double>) ?

  10. #10
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Un tableau 2D serait std::vector<std::vector<double>>. Ce qui est généralement déconseillé pour les performances (mais c'est mieux qu'un fichier). L'une des approches les plus utilisées est un std::vector linéarisée.
    FAQ C++ - Comment implémenter un opérateur d'indexation pour une classe Matrix ?
    FAQ C++ - Pourquoi est-il préférable que l'interface de ma classe Matrix ne soit pas basée sur le modèle du tableau de tableaux ?

    Citation Envoyé par JulDev Voir le message
    j'ai vu istringsteam (j'ai l'impression que ça permet de stocker un flux complet)
    Oui mais il y a quasiment les mêmes soucis que pour le fichier. Sauf si tu as déjà une fonction qui fait le travail avec un stream, ne te lance pas dedans et utilise le tableau 2D de double.

  11. #11
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ces deux classes n'ont rien à voir.
    vector<double> est un tableau dont la taille change avec le besoin.
    istringstream est un stream de lecture (input) sur une chaine.

    Le premier permet de stocker des doubles, tandis que le second permet de les extraire d'une chaine de caractères.

    Mais le stream peut servir à lire une chaine contenant quatre doubles, en les mettant dans un vector.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  12. #12
    Candidat au Club
    Homme Profil pro
    Ingénieur systèmes et réseaux
    Inscrit en
    Septembre 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Eure (Haute Normandie)

    Informations professionnelles :
    Activité : Ingénieur systèmes et réseaux
    Secteur : High Tech - Produits et services télécom et Internet

    Informations forums :
    Inscription : Septembre 2014
    Messages : 5
    Points : 2
    Points
    2
    Par défaut
    Ok, merci pour vos réponses. Je vais tester les différentes solutions !

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 1
    Dernier message: 21/12/2006, 13h06
  2. lecture d'un fichier binaire
    Par booby dans le forum C
    Réponses: 17
    Dernier message: 20/09/2006, 16h11
  3. lecture d'un fichier binaire
    Par Tonta dans le forum C++
    Réponses: 1
    Dernier message: 14/04/2006, 06h53
  4. lecture d'un fichier binaire en VB
    Par olivier] dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 28/12/2005, 11h17
  5. Lecture d'un fichier binaire
    Par Gloubie dans le forum Langage
    Réponses: 9
    Dernier message: 05/12/2005, 12h51

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo