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 :

problème de surcharge operateur '+' et '='


Sujet :

C++

  1. #21
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    avais tu pensé à faire tes tests en mode release (visual studio est connu pour faire beaucoup de choses en plus en mode débug )[/QUOTE]

    je ne suis pas sous Visual C ! je suis sur Mac (et accessoirement sous Linux) ! : GCC !

  2. #22
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Koala01,
    Désolé mais j'ai refait les tests sur un release :

    operator= traditionnel :
    durée 0.132424
    durée 0.133042
    durée 0.132578

    copy and swap :
    durée 0.149837
    durée 0.149483
    durée 0.150323

    A noter également quand même qu'il n'y a pas pour l'instant de gestion d'erreur dans la fonction 'operator=' standard qui va sans doute être freinée par cet ajout. Mais si tu regardes les 2 fonctions, il est évident que la version classique est plus efficace, les affectations sont directes alors que la version swap passe forcément par une variable intermédiaire donc une affectation supplémentaire et des appels de fonction supplémentaires. Ce qui me surprend ce sont tes résultats qui paraissent dire le contraire. Je pense que ça dépend probablement du compilateur.

    gl : la signature de la fonction 'operator=' est différente lorsque tu utilises le "copy and swap". Dans ce cas, tu ne passes pas le paramètre par référence (&m), parce que tu veux forcer la copie par le constructeur de copie pour disposer des valeurs (et non de l'adresse) de la matrice dans la pile pour effectuer ton swap.

    Donc la fonction standard a pour signature
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    matrice& operator=(const matrice &m);
    et la fonction 'copy and swap'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    matrice& operator=(const matrice m);
    Quoiqu'il en soit, le 'copy and swap' est tout à fait génial. Quand on voit la difficulté de programmation de certaines fonctions 'operator=' qui sont un véritable casse tête, il n'y a pas de raison de s'en priver.

    En tout cas merci à tous, cette discussion m'a permis de faire une révision accélérée du C++ !

  3. #23
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 413
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 413
    Par défaut
    Citation Envoyé par Peter57 Voir le message
    et la fonction 'copy and swap'
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    matrice& operator=(const matrice m);
    La copie ne doit pas être const, car ça empêcherait de swapper.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  4. #24
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    La copie ne doit pas être const, car ça empêcherait de swapper.
    exact mais curieusement ça fonctionne très bien sur mon compilateur ??
    Pour passer de la version classique à la version 'swap' je change juste le '&' !!!

    En fait on ne touche pas à la matrice passée en argument mais seulement à sa copie dans la pile. Le mot 'const' ne se justifie donc effectivement pas dans cette configuration puisqu'on ne fournit pas l'adresse de l'argument mais une copie de sa valeur. Et apparemment le compilateur ne tient pas compte du 'const 'dans ce cas. Essaye sur Visual C, tu me diras si ça passe ou pas.

  5. #25
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 413
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 413
    Par défaut
    Pour les paramètres passés par valeur, const dans la déclaration ne veut rien dire, c'est la définition qui compte.

    Par contre, sous Visual, si déclaration et définition ne sont pas identique, il y aura erreur d'édition de liens (du moins, sur les anciennes versions; je n'ai pas testé sur une version récente).
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  6. #26
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Pour les paramètres passés par valeur, const dans la déclaration ne veut rien dire, c'est la définition qui compte.

    Par contre, sous Visual, si déclaration et définition ne sont pas identique, il y aura erreur d'édition de liens (du moins, sur les anciennes versions; je n'ai pas testé sur une version récente).
    Exact, 'const' ne veut rien dire dans ce cas. Par contre mon compilo semble tout simplement ne pas en tenir compte.
    Quand j'ai testé la methode 'copy and swap', je n'ai pas réécrit la déclaration de la fonction 'operator=' mais simplement supprimé le '&'. Le const est donc resté là par erreur. Le compilateur apparemment s'en fiche complètement. Ca semble logique comme comportement. Et il ne prend pas ombrage de la différence de signatures, je pense qu'il considère simplement tout passage par valeur comme 'const' pas défaut.

  7. #27
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par Médinoc Voir le message
    Je ne vois pas trop quelle différence supprimer la forme optimisée du copy-and-swap causerait ici, je ne vois pas d'endroit où on utilise l'opérateur = avec un temporaire...
    Je ne parlais pas ici de la version copy-and-swap (i.e. matricesc) mais de la version "à la main" où le code de koala construit une copie avant d'affecter cette copie à this.

    Citation Envoyé par Peter57
    gl : la signature de la fonction 'operator=' est différente lorsque tu utilises le "copy and swap". Dans ce cas, tu ne passes pas le paramètre par référence (&m), parce que tu veux forcer la copie par le constructeur de copie pour disposer des valeurs (et non de l'adresse) de la matrice dans la pile pour effectuer ton swap.
    Oui je sais. Et ce n'est pas de celle-ci que je parlais

  8. #28
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    Citation Envoyé par koala01 Voir le message

    Comme je ne voulais pas forcément garder ce que j'ai fait pour un usage ultérieur, je me suis retrouvé avec un code ressemblant à...
    Avec ce code, moi, j'obtiens du code qui ne s'exécute pas, ce qui est normal, puisque le constructeur par copie de matrice fait n'importe quoi (delete sur un pointeur non initialisé).

    Sinon, j'ai enlevé la copie inutile dans l'opérateur= de matrice, j'ai ajouté le destructeur qui manquait, j'ai joué sur la taille des matrices, voir si ça changeait des choses, et surtout, fidèle à mon idée, j'ai fait une troisième version avec vector. Les résultats sont clairs :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    exÚcution     0 : non swap =   23.394 swap =    22.75 vector =    2.608
    exÚcution     1 : non swap =   23.347 swap =   22.782 vector =    2.595
    exÚcution     2 : non swap =   23.347 swap =   22.797 vector =    2.604
    exÚcution     3 : non swap =   23.349 swap =   22.779 vector =    2.603
    exÚcution     4 : non swap =   23.352 swap =   22.759 vector =    2.602
    exÚcution     5 : non swap =   23.362 swap =   22.811 vector =    2.596
    exÚcution     6 : non swap =    23.35 swap =   22.755 vector =    2.598
    exÚcution     7 : non swap =   23.341 swap =   22.773 vector =    2.599
    exÚcution     8 : non swap =   23.376 swap =   22.756 vector =    2.598
    exÚcution     9 : non swap =   23.348 swap =   22.757 vector =    2.601
    Pourquoi une telle différence ? Je pense que c'est avant tout parce que pour le vector, sans rien faire, on bénéficie de la sémantique de déplacement.

    Alors, certes, c'est "de la triche", mais en fait, pas tant que ça : Mon but était de prouver que du code écrit plus naturellement, plus simplement, avec moins de risques de bugs (j'en ai quand même trouvé dans les deux versions précédentes...) était aussi plus rapide quand on réutilisait des composants prévus pour.

    Mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    #include <iostream>
    #include <iomanip>
    #include <ctime>
    #include <algorithm>
    #include <vector>
    class matrice{
    public :
        matrice() { _li=0; _col=0; _val = 0;};
        matrice(int li, int col);
        matrice(const matrice &m); // cons de copie
    	~matrice();
        float& operator()(int li, int col); // setter
        float operator()(int li, int col) const; // getter
        matrice& operator=(matrice const &m);
        matrice operator+(const matrice & m2);
        int lignes(){ return _li;};
        int colonnes(){ return _col;};
    private :
        int _li, _col;
        float *_val;
    };
    class matricesc{
    public :
        matricesc() { _li=0; _col=0; _val = 0;};
        matricesc(int li, int col);
        matricesc(const matricesc & m); // cons de copie
    	~matricesc();
        float& operator()(int li, int col); // setter
        float operator()(int li, int col) const; // getter
        matricesc& operator=( matricesc m);
        matricesc operator+(const matricesc & m2);
        int lignes(){ return _li;};
        int colonnes(){ return _col;};
    private :
        void swap(matricesc & other);
        int _li, _col;
        float *_val;
    };
     
    class matricev{
    public :
        matricev() : _li(0), _col(0){}
        matricev(int li, int col);
        float& operator()(int li, int col); // setter
        float operator()(int li, int col) const; // getter
        matricev operator+(const matricev & m2);
        int lignes(){ return _li;};
        int colonnes(){ return _col;};
    private :
        int _li, _col;
        std::vector<float> _val;
    };
     
    struct times{
    clock_t first;
    clock_t second;
    clock_t third;
    };
     
    int const taille=100;
     
    template<class Matrice> void test(clock_t &result)
    {
        Matrice m(taille,taille);
        Matrice n;
     
        for(int i = 0; i<taille; i++)
        for(int j=0; j<taille; j++)
        m(i,j) = i*taille + j;
     
        clock_t	t;
        t = clock();            // initialisation T0
     
        for (int i = 0; i<1000000; i++)
        n = m;
     
        t = clock()-t;         // t1 -t0 => nobre de clicks par seconde
        result = t;
    }
     
    int main(){
        std::vector <times> tab;
        for(int turn = 0;turn<10;++turn){
            times currentTime;
    		test<matrice>(currentTime.first);
    		test<matricesc>(currentTime.second);
    		test<matricev>(currentTime.third);
     
            tab.push_back(currentTime);
        }
        for(size_t i = 0;i< tab.size(); ++i){
            double first  = (double)tab[i].first/CLOCKS_PER_SEC;
            double second = (double)tab[i].second/CLOCKS_PER_SEC;
    		double third = (double)tab[i].third/CLOCKS_PER_SEC;
            std::cout<< "exécution " <<std::setw(5) <<i
    			<< " : non swap = " << std::setw(8) << first
    			<< " swap = " << std::setw(8) << second 
    			<< " vector = " << std::setw(8) << third
    			<< std::endl;
     
     
        }
        return 0;
    }
     
    /// ---------------------------- matrice
    matrice::matrice(int li , int col){
        _li = li;
        _col= col;
        _val = new float[_li * _col];
    }
    matrice::matrice(const matrice &m){
        _li = m._li;
        _col = m._col;
        int n = _li*_col;
        _val = new float[n];
        for(int i=0; i<n; i++)
            _val[i] = m._val[i];
    }
    matrice matrice::operator+(const matrice &m2){
        matrice temp(*this);
        int n = temp._li * temp._col;
        for(int i = 0; i<n; i++)
            temp._val[i] = _val[i] + m2._val[i];
        return temp;
    }
    matrice &matrice::operator=(matrice const &m){
        if(this !=&m)
        {
            delete [] _val;
            _li = m._li;
            _col = m._col;
            int n = _li*_col;
            _val = new float[n];
            for(int i = 0; i<n; i++)
                _val[i] = m._val[i];
        }
        return *this;
    }
    matrice::~matrice()
    {
    	delete[] _val;
    }
    float& matrice::operator()(int l, int c){
    return _val[l*_col+c];
    }
    float matrice::operator()(int l, int c)const{
    return _val[l*_col+c];
    }
     
    /// ---------------------------- matricesc
     
    matricesc::matricesc(int li , int col){
        _li = li;
        _col= col;
        _val = new float[_li * _col];
    }
    matricesc::matricesc(const matricesc &m){
        _li = m._li;
        _col = m._col;
        int n = _li*_col;
        _val = new float[n];
        for(int i=0; i<n; i++)
            _val[i] = m._val[i];
    }
    matricesc matricesc::operator+(const matricesc &m2){
        matricesc temp(*this);
        int n = temp._li * temp._col;
        for(int i = 0; i<n; i++)
            temp._val[i] = _val[i] + m2._val[i];
        return temp;
    }
    matricesc::~matricesc()
    {
    	delete[] _val;
    }
    matricesc &matricesc::operator=( matricesc m){
       m.swap(*this);
        return *this;
    }
    void matricesc::swap(matricesc &m){
    	std::swap(_li, m._li);
    	std::swap(_col, m._col);
    	std::swap(_val, m._val);
    }
     
    float& matricesc::operator()(int l, int c){
    return _val[l*_col+c];
    }
    float matricesc::operator()(int l, int c)const{
    return _val[l*_col+c];
    }
     
    /// ---------------------------- matricev
     
    matricev::matricev(int li , int col) :
    	_li(li),
    	_col(col),
    	_val(li*col)
    {
    }
    matricev matricev::operator+(const matricev &m2){
        matricev temp(*this);
        int n = temp._li * temp._col;
        for(int i = 0; i<n; i++)
            temp._val[i] = _val[i] + m2._val[i];
        return temp;
    }
     
    float& matricev::operator()(int l, int c){
    return _val[l*_col+c];
    }
    float matricev::operator()(int l, int c)const{
    return _val[l*_col+c];
    }
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  9. #29
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Koala01,
    Ton code beugue chez moi. Pas le temps aujourd'hjui de regarder ca de plus près.
    J'ai vérifié mes fonctions , actuellement elles sont identiques à celles que tu utilises. J'ai été intéressé par l'utilisation de la classe vector.
    Merci d'optimiser mon code !

  10. #30
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par gl Voir le message
    Attention, tu as introduit un savré biais dans ton code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    matrice &matrice::operator=(matrice m)
    devrais être

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    matrice &matrice::operator=(const matrice& m)
    Je te renvoies, sur ce point, à cette discussion dans laquelle on précise que les deux possibilités sont tout à fait bonne, bien que l'opérateur = prenant un objet permette le cas échéant de profiter d'une élision de la copie.

    Comme je l'expliquais dans cette discussion, l'élision de copie n'est de toutes manières possible que si le type ne doit pas se charger d'allocations dynamiques, et n'est donc pas envisageable dans le cas présent

    J'ai donc, par acquit de conscience, vérifié en modifiant l'opérateur d'affectation de manière à ce qu'il prenne une référence constante sur un objet de type matricsc (qui est le type qui utilise le copy and swap), et les résultats restent malgré tout fort similaires, même si certains ratios sont juste inférieurs à 2 (de l'ordre de 1.8 à 1.9)

    Mais je ne vois pas quel biais j'aurais pu introduire en passant l'objet par valeur (ce qui implique donc une copie d'office) plutôt qu'en le passant par référence

    Au temps pour moi, tu parlais de l'opérateur de martice et non de matricesc...

    Tout ce que j'ai donc remodifié le code afin de prendre ton avis en compte, de manière à permettre d'éviter la copie en cas d'auto affectation.

    En mode débug, on observe bel et bien une inversion des ratios (la version sans copie and swap étant plus rapide avec des ratios allant de 1.02 à 1.1), par rapport au mode release (pour lequel la version copy and swap est plus rapide avec des ratio compris entre 1.15 et 1.38).

    Cela nous mène donc à la deuxième règle de base des benchmarks (que peter ne semble pas avoir pris en compte): un bench s'effectue en mode release et non en mode debug

    PS: j'ai modifié le code de mon intervention précédente pour tenir compte des modifications
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  11. #31
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Koala01,
    Je ne veux pas te harceler ni chercher à tout prix à avoir raison. Tu es bien plus fort que moi en programmation, c'est évident.
    Ton code ne fonctionne pas chez moi. J'ai donc repris ta programmation de la classe matricev et j'ai retesté une fonction 'main' juste un peu modifiée. J'ai ajouté une affectation à l'intérieur de la boucle de 1 000 000 itérations pour être sûr que le compilateur n'effectue pas une optimisation du style
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    n=m;
    for(int i = 0; i<1000000; i++)
    	;
    à la place de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    for(int i = 0; i<1000000; i++)
    	n=m;
    Voici le code de main. Pour la classe matrice je me suis contenté de faire un copier-coller de ta classe et de changer 'matricev' en 'matrice'.

    Main
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
     
    #include <iostream>
    #include <ctime>
    #include "matrice.h"
     
    using namespace std;
     
    int main()
    {
        matrice m(3,3);
    	matrice n;
     
        for(int i = 0; i<3; i++)
            for(int j=0; j<3; j++)
                m(i,j) = i*3 + j;
     
    	clock_t	t;   
     
    	for(int times=0; times <5; times++)
    		{
    		t = clock();            // initialisation T0
    		for (int i = 0; i<1000000; i++)
    			{
    			m(1,0) = float(i);
    			n = m;
    			}
    		t = clock()-t;         // t1 -t0 => nobre de clicks par seconde
    		float d = (float)t/CLOCKS_PER_SEC;  // nombre de secondes
    		std::cout <<"durée " << d << std::endl;
    		}
        return 0;
    }
    matrice.h
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    #include <algorithm>
    #include <vector>
    #include <iostream>
    #include <iomanip>
     
     
    class matrice{
    	friend std::ostream& operator<<( std::ostream& flux, matrice &m);
    	public :
        matrice() : _li(0), _col(0){}
        matrice(int li, int col);
        float& operator()(int li, int col); // setter
        float operator()(int li, int col) const; // getter
        matrice operator+(const matrice & m2);
        int lignes(){ return _li;};
        int colonnes(){ return _col;};
    	private :
        int _li, _col;
        std::vector<float> _val;
    };
    matrice.cpp
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     
    #include "matrice.h"
     
    /// ---------------------------- matrice
     
    matrice::matrice(int li , int col) :
    _li(li),
    _col(col),
    _val(li*col)
    {
    }
     
     
    float& matrice::operator()(int l, int c){
    	return _val[l*_col+c];
    }
    float matrice::operator()(int l, int c)const{
    	return _val[l*_col+c];
    }
    Voici les résultats en mode release que j'ai parfaitement pris en compte ! J'ai fait un copier-coller à partir du terminal.
    1- fonction operator=() classique
    Last login: Sun May 26 17:16:23 on ttys000
    /Users/pierre/Desktop/classique ; exit;
    imac-de-pierre:~ pierre$ /Users/pierre/Desktop/classique ; exit;
    durée 0.160282
    durée 0.159449
    durée 0.15905
    durée 0.157562
    durée 0.158578
    logout

    [Opération terminée]
    2- fonction style 'copie and swap'
    Last login: Sun May 26 17:33:57 on ttys000
    /Users/pierre/Desktop/copy_and_swap ; exit;
    imac-de-pierre:~ pierre$ /Users/pierre/Desktop/copy_and_swap ; exit;
    durée 0.158738
    durée 0.15834
    durée 0.158691
    durée 0.159487
    durée 0.159779
    logout

    [Opération terminée]
    3- matrice programmée avec vector<float>
    Last login: Sun May 26 17:36:02 on ttys000
    imac-de-pierre:~ pierre$ /Users/pierre/Desktop/test_vector ; exit;
    durée 0.165899
    durée 0.165483
    durée 0.165555
    durée 0.165397
    durée 0.165395
    logout

    [Opération terminée]
    Quand j'aurais le temps, je transfers tout ça sur linux et je recompile en linux G++.

  12. #32
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Salut Koala01 !
    Quelques remarques concernant ton code :
    - tu t'es vraiment donné le beau rôle en saisissant une matrice improbable de 100 X 100 éléments. Recopie de 10 000 éléments au lieu de 9 ! Malgré tout je joue le jeu.
    J'ai été surpris par la différence existant entre la version <vector> et les deux autres. La surprise est d'autant plus justifiée que plus la matrice est grosse plus le temps mis par ta programmation vecteur est courte !!! Ca sent l'optimisation à plein nez !
    J'ai donc repris le programme et apporté les modification suivantes :
    - d'abord j'ai éliminé la boucle de 100000 itérations et remplacé par une affectation de 10 matrices en série :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
            Matrice n[10];
            n[0] = m;
    	for(int i=1; i<10; i++)
    		n[i] = n[i-1];
    de cette façon seules les fonctions d'affectation sont prises en compte.
    J'ai "un peu" optimisé les fonctions 'operator+' du fichier matricecs et 'operator=" du fichier matrice en remplaçant la copie du tableau par std::copy :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
             /*for(int i=0; i<n; i++)
            _val[i] = m._val[i];*/
    	std::copy(m._val, m._val+n, _val);
    Le temps n'est plus en secondes mais en clics.
    J'ai fait la moyenne de 20 tests en éliminant le premier qui est constamment très élevé.

    Le résultat est sans appel !
    La version classique l'emporte (de peu quand même) devant le 'copy and swap' et la version <vector> est dernière !
    Last login: Mon May 27 22:26:18 on ttys001
    /Users/pierre/Desktop/test_vector ; exit;
    imac-de-pierre:~ pierre$ /Users/pierre/Desktop/test_vector ; exit;
    exécution 0 : non swap = 399 swap = 57 vector = 56
    exécution 1 : non swap = 57 swap = 56 vector = 59
    exécution 2 : non swap = 58 swap = 55 vector = 58
    exécution 3 : non swap = 54 swap = 61 vector = 62
    exécution 4 : non swap = 56 swap = 57 vector = 57
    exécution 5 : non swap = 57 swap = 55 vector = 58
    exécution 6 : non swap = 57 swap = 56 vector = 56
    exécution 7 : non swap = 54 swap = 55 vector = 57
    exécution 8 : non swap = 55 swap = 55 vector = 67
    exécution 9 : non swap = 55 swap = 55 vector = 56
    exécution 10 : non swap = 53 swap = 69 vector = 64
    exécution 11 : non swap = 55 swap = 54 vector = 56
    exécution 12 : non swap = 62 swap = 57 vector = 57
    exécution 13 : non swap = 56 swap = 57 vector = 58
    exécution 14 : non swap = 57 swap = 56 vector = 57
    exécution 15 : non swap = 58 swap = 66 vector = 67
    exécution 16 : non swap = 58 swap = 58 vector = 56
    exécution 17 : non swap = 57 swap = 55 vector = 55
    exécution 18 : non swap = 55 swap = 55 vector = 60
    exécution 19 : non swap = 57 swap = 55 vector = 56
    exécution 20 : non swap = 53 swap = 57 vector = 67
    moyenne sur 20 : non swap = 56.2 swap = 57.2 vector = 59.15
    logout
    [Opération terminée]
    Peter : jeu set et match !
    Bon je sors.

  13. #33
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Peter57 Voir le message
    Salut Koala01 !
    Quelques remarques concernant ton code :
    - tu t'es vraiment donné le beau rôle en saisissant une matrice improbable de 100 X 100 éléments. Recopie de 10 000 éléments au lieu de 9 ! Malgré tout je joue le jeu.
    J'ai été surpris par la différence existant entre la version <vector> et les deux autres. La surprise est d'autant plus justifiée que plus la matrice est grosse plus le temps mis par ta programmation vecteur est courte !!! Ca sent l'optimisation à plein nez !
    Rendons à César-JolyLoic ce qui lui appartient, veux tu

    C'est lui qui a présenté une version de code utilisant std::vector et qui a, effectivement, travaillé sur des matrices de 100*100

    Ceci dit, en utilisant les bonnes méthodes de la classe vector (comme le constructeur prenant un nombre d'éléments ou la fonction reserve si l'on doit attendre d'avoir une idée relativement précise du nombre d'éléments que l'on voudra placer dedans), la création du tableau sous jascent est sensée être en temps constant, et la copie peut (ou devrait pouvoir) se faire "d'une traite" avec une fonction de l'ordre des memcpy et autres

    Il n'est donc pas forcément impensable que cela corresponde effectivement à une optimisation réelle.

    Ceci dit, la réduction du code à écrire est tout à fait normale, car la classe std::vector est prévue pour être copiable de manière transparente pour l'utilisateur.

    A partir de là, tout ce qui peut poser problème en terme de gestion dynamique de la mémoire peut, purement et simplement, être "oublié" par l'utilisateur alors se concentrer sur les problèmes propres au projets sur lequel il travaille.

    C'est (en partie du moins) ce que l'on appelle l'encapsulation, ce qui permet la ré-utilisabilité du code et, surtout, le propre de n'importe quelle bibliothèque, qu'elle soit écrite en C ou en C++, fournie par le langage ou créée de tes propres mains

    La grosse différence qui existera entre une bibliothèque comme la S(T)L et ta bibliothèque personnelle étant que la S(T)L est "martyrisée" par un nombre de personnes qui permet de rapidement trouver les erreurs éventuelles, voire, de proposer des optimisations, justement.

    [EDIT]Note que, si c'est pour n'avoir une différence que de dix ticks, j'aime autant prendre la version qui utilise des std::vector car elle est vraiment de nature à m'éviter bien des cheveux blancs, alors que pour voir une différence significative (allez, mettons d'une seconde), il faudra faire de très nombreuses itérations
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  14. #34
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Citation Envoyé par koala01 Voir le message

    La grosse différence qui existera entre une bibliothèque comme la S(T)L et ta bibliothèque personnelle étant que la S(T)L est "martyrisée" par un nombre de personnes qui permet de rapidement trouver les erreurs éventuelles, voire, de proposer des optimisations, justement.

    [EDIT]Note que, si c'est pour n'avoir une différence que de dix ticks, j'aime autant prendre la version qui utilise des std::vector car elle est vraiment de nature à m'éviter bien des cheveux blancs, alors que pour voir une différence significative (allez, mettons d'une seconde), il faudra faire de très nombreuses itérations
    Tout à fait d'accord avec toi. En plus la STL a l'air d'être sacrément optimisée ! j'ai d'abord tenté d'optimiser la copie en programmant moi même la fonction copy de façon efficace, je pense.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
    inline void copy(float *deb, float *fin, float *dest){
    	while(deb != fin)
    		*dest++ = *deb++;
    }
    Peux pas faire mieux ! J'ai été très déçu ! snif !
    Résultat : 360 clicks au lieu de 56 avec la fonction std::copy ! (J'aimerais bien voir le source de cette fonction !!!)
    Il faudrait voir l'optimisation du code par le compilateur mais je ne suis pas sûr d'arriver au même résultat. Autant utiliser la STL.
    Bien cordialement.
    Peter.

  15. #35
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 413
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 413
    Par défaut
    La version "artisanale" de operator= est-elle exception-safe? (pour chaque point susceptible de lancer une exception, pas de fuite ni de risque d'état incohérent de l'objet)

    (pour un truc qui ne fait qu'une seule allocation, il y a des chances que oui)
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  16. #36
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Médinoc,
    A priori je dirais que oui. La programmation est tout de même très 'standard', voire basique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    matrice &matrice::operator=(matrice const &m){
        if(this !=&m)
    	{
            delete [] _val;
            _li = m._li;
            _col = m._col;
            int n = _li*_col;
            _val = new float[n];
    	std::copy(m._val, m._val+n, _val);
    	}
        return *this;
    }

  17. #37
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Peter57 Voir le message
    Médinoc,
    A priori je dirais que oui. La programmation est tout de même très 'standard', voire basique.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    matrice &matrice::operator=(matrice const &m){
        if(this !=&m)
    	{
            delete [] _val;
            _li = m._li;
            _col = m._col;
            int n = _li*_col;
            _val = new float[n];
    	std::copy(m._val, m._val+n, _val);
    	}
        return *this;
    }
    Heuuu... juste un truc: que se passe-t-il si (pour une raison ou une autre) new float[n] vient à échouer

    C'est très bien, tu évites le problème de l'auto affectation, mais... tu n'as strictement aucune certitude quant au fait que l'allocation dynamique réussira.

    Le problème sera alors que... tu as perdu toutes les valeurs précédentes vu que tu en a déjà libéré la mémoire(re )
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  18. #38
    Membre averti
    Profil pro
    Inscrit en
    Février 2007
    Messages
    63
    Détails du profil
    Informations personnelles :
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Février 2007
    Messages : 63
    Par défaut
    Mon cher Koala01,
    je ne suis pas sûr que tu aies bien vu sur quel forum nous nous trouvons !
    Suis mon regard tout là haut :
    Forum du club des développeurs et IT Pro > C et C++ > C++ > Débuter

    Ou alors c'est une forme de bizutage pour les nouveaux ?
    Je suis un amateur (pas informaticien mais médecin !) qui n'a plus écrit une seule ligne de code depuis plus de 20 ans. En outre, comme je l'ai dit plus haut je ne suis vraiment pas un spécialiste du c++.
    J'ai écrit cette classe pour me remémorer de très vieilles notions oubliées et pour tenter de me souvenir des pièges comme l'utilisation d'un pointeur non attribué ou le renvoi d'une référence sur un objet temporaire. Ca commence à revenir comme tu vois.
    Laisse moi encore un peu de temps et je te fais la version avec les throw et les catch qui vont bien.
    Mais vu que je n'ai nullement l'intention de me lancer dans un projet commercial de plusieurs années, le fait que ça risque de planter une fois par échec de l'allocation dynamique ne m'empêche pas de dormir !
    Merci tout de même pour tes conseils.

  19. #39
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 413
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 413
    Par défaut
    Vu qu'ici seul le new float[n] peut lancer une exception, ce code peut être rendu exception-safe sans copy-and-swap facilement: Il suffit de mettre l'allocation en premier.

    Mais dès qu'il y a deux points capable de lancer des exceptions et deux ressources à gérer, c'est beaucoup plus dur. Une simple classe contenant deux matrices n'aura pas un opérateur = exception-safe sans une grande prise de tête: Avec l'implémentation naîve ci-dessous, la seconde matrice peut lancer une exception après que la première ait été affectée!

    Code C++ naïf : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    DeuxMatrices & DeuxMatrices::operator=(DeuxMatrices const &src)
    {
    	mat1 = src.mat1;
    	mat2 = src.mat2; //Si on a une exception ici, crac!
    }
    L'emploi du copy-and-swap résoudra le problème.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  20. #40
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 54
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Citation Envoyé par Peter57 Voir le message
    Mon cher Koala01,
    je ne suis pas sûr que tu aies bien vu sur quel forum nous nous trouvons !
    Suis mon regard tout là haut :

    Ou alors c'est une forme de bizutage pour les nouveaux ?
    Je suis un amateur (pas informaticien mais médecin !) qui n'a plus écrit une seule ligne de code depuis plus de 20 ans. En outre, comme je l'ai dit plus haut je ne suis vraiment pas un spécialiste du c++.
    J'ai écrit cette classe pour me remémorer de très vieilles notions oubliées et pour tenter de me souvenir des pièges comme l'utilisation d'un pointeur non attribué ou le renvoi d'une référence sur un objet temporaire. Ca commence à revenir comme tu vois.
    Laisse moi encore un peu de temps et je te fais la version avec les throw et les catch qui vont bien.
    Mais vu que je n'ai nullement l'intention de me lancer dans un projet commercial de plusieurs années, le fait que ça risque de planter une fois par échec de l'allocation dynamique ne m'empêche pas de dormir !
    Merci tout de même pour tes conseils.
    Ne t'en fais pas, je sais parfaitement dans quelle section nous nous trouvons

    Plutôt que de te répondre "sêchement" quelque chose du genre de "ben non, justement, tu te goures", je te pose une question pour te permettre de suivre le raisonnement par toi même.

    Je reconnais que ton approche qui consiste à évite l'auto acceptation est correcte, mais que bah, voilà... tu n'as strictement aucune garantie de réussite à l'appel de new.

    Quelque part, le fait que new lance une exception est ici ce qui peut encore t'arriver de mieux car ton application "planterait" purement et simplement.

    Simplement, ta réponse "A priori je dirais que oui." n'est déjà pas bonne en soi, et cela méritait, même dans la section "débuter" d'être mis en évidence et d'être un minimum expliqué.

    Je ne voulais absolument pas t'agresser -- en me connaissant un peu, tu te rendras compte que ce n'est pas mon style, même si je suis parfois un peu direct -- et si tu l'as ressenti comme cela, je t'en fais mes plus plates excuses.

    Cependant, il faut savoir, vu que tu as mis les try catch sur le tapis, que leur utilisation, en l'état actuel de ton code et de l'ordre des instructions fera encore pis que mieux, car, si elle pourra sans doute éviter que ton application ne plante, elle laissera ton objet dans un état incohérent, au départ duquel il te sera impossible de revenir à "l'état initial" (cf la dernière ligne de mon intervention précédente) et que tu te trouveras donc sur une voie toute tracée qui, tôt ou tard, mènera à un comportement indéfini (vraisemblablement une erreur de segmentation, vu que c'est généralement ce qui arrive quand on essaye de déréférencer un pointeur invalide).

    Ce genre de problème est généralement très difficile à résoudre, pour la simple et bonne raison qu'il peut survenir très loin du point où la cause prend naissance.

    Tant qu'à se remémorer les base, autant essayer de se rappeler les problèmes sous-jacents, tu ne crois pas
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

+ Répondre à la discussion
Cette discussion est résolue.
Page 2 sur 3 PremièrePremière 123 DernièreDernière

Discussions similaires

  1. Problème de surcharge d'operateur
    Par CodeurNé dans le forum C++
    Réponses: 2
    Dernier message: 29/12/2012, 18h51
  2. Problème de surcharge d'operateur <
    Par Shuret dans le forum C++Builder
    Réponses: 6
    Dernier message: 11/04/2008, 18h16
  3. Petit probléme de surcharge d'opérateur .
    Par Clad3 dans le forum C++
    Réponses: 20
    Dernier message: 11/04/2005, 20h15
  4. Problème de surcharge d'opérateurs
    Par Hell dans le forum C++
    Réponses: 17
    Dernier message: 17/01/2005, 16h01
  5. Réponses: 2
    Dernier message: 25/07/2004, 23h24

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