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

Langage C++ Discussion :

Vecteur contre tableau : quelques tests


Sujet :

Langage C++

  1. #1
    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 Vecteur contre tableau : quelques tests
    Salut,
    J'entend souvent deux sons de cloche, celui des gens d'internet qui disent un peu partout que std::vector (et les autres conteneurs modernes)
    n'entrainent pas de perte de performances par rapport aux tableaux dynamiques. et ceux du/des collègues tendances barbus intégristes et adeptes du C with classes car les conteneurs STL sont beaucoup trop lents d'après eux.

    J'ai donc fait un petit test que je partage avec vous. C'est assez intéressant, tant au niveau des perfs de std::vector que du coût du mode Debug contre le mode optimisé

    J'ai un rempli un vecteur (values) de 15 000 000 de nombres aléatoire, et je vais en calculer l'intégrale de 3 facons différentes
    Tableau dynamique C With classes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    clock_t before = clock();
    long* integraltab =new long[NEntries];
    memset(integraltab, 0, NEntries*sizeof(long)); 
    integraltab[0]=values[0];
    for (unsigned int i =1; i < NEntries; i++) { 
      integraltab[i]=integraltab[i-1]+values[i];
    }
    clock_t after = clock();

    Index loop sur un vecteur CPP + STL
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    before = clock();
    vector<long> integralvec2(NEntries);
    integralvec2[0]=values[0];
    for (unsigned int i =1; i < NEntries; i++) { 
      integralvec[i]=integralvec[i-1]+values[i];
    }
    after = clock();
    En principe c'est le même code que le précédent, ce devrait donc être assez indépendant de mon niveau

    Range based loop Cpp11
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    before  = clock();
    vector<long> integralvec;
    integralvec.reserve(NEntries);
    long previousvalue = 0; 
    for (const auto& val : values) { 
      integralvec.push_back(val+previousvalue);
      previousvalue = val;
    } 
    after = clock();
    je ne suis pas un spécialiste des range based loop, j'ai donc pas forcément la meilleure méthode pour acceder à la valeur précédente.

    J'ai la flemme de rajouter les itérateurs à mon tests

    Les résultats

    * En mode Debug (visual studio mode Debug, gcc -g -O0 )
    * gcc 4.6.3 (virtualisé)
    [TAB] Integral was computed in 210ms
    We had : 15000000 entries
    [VECTOR / index based loop] Integral was computed in 320ms
    We had : 15000000 entries
    [VECTOR / Range based loop] Integral was computed in 650ms
    We had : 15000000 entries
    * MS Visual studio 2012 (Natif)
    [TAB] Integral was computed in 1013ms
    We had : 15000000 entries
    [VECTOR / index based loop] Integral was computed in 2911ms
    We had : 15000000 entries
    [VECTOR / Range based loop] Integral was computed in 14812ms
    We had : 15000000 entries
    On voit donc que les range based loop dégradent les perfs en débug, surtout avec visual studio. et que l'accès à un vecteur est sur les deux compilateurs environ 3 fois plus lent qu'à un tableau

    * En mode Optimisé (visual studio mode Release, gcc -O3 )
    * gcc 4.6.3 (virtualisé)
    [TAB] Integral was computed in 140ms
    We had : 15000000 entries
    [VECTOR / index based loop] Integral was computed in 120ms
    We had : 15000000 entries
    [VECTOR / Range based loop] Integral was computed in 110ms
    We had : 15000000 entries
    * MS Visual studio 2012 (Natif)
    [TAB] Integral was computed in 67ms
    We had : 15000000 entries
    [VECTOR / index based loop] Integral was computed in 76ms
    We had : 15000000 entries
    [VECTOR / Range based loop] Integral was computed in 83ms
    We had : 15000000 entries

    Bonne nouvelle, la compilation optimisée ramène les performances de std::vector à quelque chose de comparable avec un tableau dynamique (avec tout de même une perte d'environ 10% sous visual). (Sachant qu'il faudrait faire des moyennes sur 1000 essais pour avoir quelques chose de valide)
    Il semblerait que gcc s'en sorte mieux avec C++11 (y aurait-il des optimisations cachées non faisable avec un tableau ?) que visual C++. Par contre en mode debug, on sent fortement le coup en perf d'un vecteur comparé à un tableau. On notera au passage que sur un tableau brut, gcc perd 50% de perf en debug, contre un facteur 8-9 avec visual (Par contre je m'aventurerai pas à dire si c'est l'un qui est mauvais en debug ou l'autre qui est mauvais en optimisation)


    Je laisse les experts du forum commenter...

  2. #2
    Membre confirmé Avatar de KsassPeuk
    Homme Profil pro
    Ingénieur Chercheur
    Inscrit en
    Juillet 2013
    Messages
    138
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Chercheur
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2013
    Messages : 138
    Points : 635
    Points
    635
    Par défaut
    Lu'!

    On optimise quand on sait où on doit optimiser.
    Donc on optimise quand on peut faire un profiling représentatif.
    Donc on optimise quand le programme est à peu prêt complet, et stable.

    Faire un code stable est bien plus facile en utilisant les possibilités offertes par la STL. Si ta conception est bonne, elle doit respecter le DIP et donc chacune des couches de ton application dépendra d'abstractions dont il sera toujours temps de changer les implémentations si elles sont insuffisamment performantes. Par exemple, pour ton vector, s'il est tranquillement installé dans l'implémentation d'une fonctionnalités, et que finalement, c'est un vector d'élément de type simple (int, float, double ...), qui ne nécessitent pas d'initialisation (par exemple, si on les écrit la seconde d'après depuis un autre conteneur), il sera toujours temps de faire un malloc/free s'il s'avère que ça peut apporter un vrai gain.

  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
    - compiler en Debug ?
    - utilité du ranged-loop dans ce cas ?

    Ton test ne montre absolument rien en fait.
    La ranged-loop est plus lente ? Bah faut dire qu'elle a pas du tout le même code que les autres boucles, donc comparer des bananes et des orties euh..

    vector est plus lent ? Bah oui, y'a une indirection puisqu'on utilise une classe. D'où qu'on fait les benchmark en release, pour commencer sur un semblant de truc correct et valable/probant.
    Si tu veux prouver encore mieux que vector est mauvais face à un tableau "C-style", utilise la fonction at() et plus l'opérateur [], tu auras ton bonheur !

    Si values est déjà un tableau contigu de données, autant faire un memcpy.
    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
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    (HS : on est a GCC 4.9.1, bientôt 5.0, et MSVC 2014, bientôt 2015. C'est bien d'utiliser des compilateurs a jour quand on est intéressé par les performances, pour bénéficier des optimisations faites dans les compilateurs)

    En gros, tes tests montrent que des codes qui font des choses différentes ont des performances différentes. Ok, c'est un point de départ...

    En plus des remarques de mes collègues.

    Tu compares du code qui fait pleins de vérifications (vector) et du code qui n'en fait pas (a quel moment tu vérifies que ton new a réussit ? A quel moment tu vérifies que ton memset a réussit ? etc). C'est probablement une des raisons principales qui fait que les codes "C with class" semblent plus performants : ce sont de mauvais codes, qui vivent dans le monde merveilleux de bisounours sans erreurs.

    Ensuite, un vector::reserve et une new[] ne font pas la même chose non plus. Le premier alloue une "réserve" en mémoire, ce qui implique un test a chaque push_back pour vérifier s'il faut augmenter la taille ou non. A cela, il faut ajouter le fait qu'un vector initialise ses éléments.

    Donc oui, tes barbus ont raisons : un code C with class mal écrit sera plus rapide. Sur le court terme. Parce que a long terme, tu passeras beaucoup plus de temps a debuger tes codes mal écrit. Parce que c'est typiquement le genre de problématiques qui produisent des comportements indéterminés, parfois très compliqué a debuger (non reproductible, plantage qui arrive de temps en temps, mais qui est critique) et que l'on peut traîner pendant des jours, voire des semaines.
    Ce n'est absolument pas une stratégie stupide que d'accepter une perte de quelques % de performances, pour gagner des semaines ou mois de debugage/maintenance du code.

    @KsassPeuk : std::get_temporary_buffer au lieu de malloc/free pour un buffer non initialisée

  5. #5
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Les perfs en debug, OSEF.
    Ce qui compte c'est en release.

    Avec ceci:
    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
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    #include <iostream>
    #include <vector>
    #include <iterator>
    #include <ctime>
    #include <sys/timeb.h>
    #include <cstdio>
    #include <cstdlib>
    #include <algorithm>
    #include <cassert>
    #include <cstring>
     
    // const size_t K = 200;
    const size_t K = 5;
    const int nXLoop = 8000;
    const int nYLoop = nXLoop; //Must be the same for both tests
     
     
    struct timeb start;
    struct timeb end;
    double delta;
     
    inline void endTimer(){
        ftime(&end);
        delta = end.time-start.time;
        delta *= 1000;
        delta += end.millitm-start.millitm;
        delta /= 1000;
    }
     
    struct NMatrix
    {
        typedef int real;
        NMatrix(std::size_t L_, std::size_t C_, bool zero_=true)
        : m_buffer(new real[L_*C_]), m_L(L_), m_C(C_)
        {
            // if(zero_) std::fill_n(&m_buffer[0], m_L*m_C, real());
            if(zero_) std::memset(&m_buffer[0], 0, m_L*m_C*sizeof(real));
        }
        ~NMatrix() {delete[] m_buffer;}
        NMatrix(NMatrix const& rhs_)
        : m_buffer(new real[rhs_.m_L*rhs_.m_C]), m_L(rhs_.m_L), m_C(rhs_.m_C)
        {
            std::copy(&rhs_.m_buffer[0], &rhs_.m_buffer[m_L*m_C], &m_buffer[0]);
        }
        NMatrix& operator=(NMatrix rhs_) {
           this->swap(rhs_);
           return *this;
        }
        real const& operator()(size_t l_, size_t c_) const {
            assert(l_ < m_L);
            assert(c_ < m_C);
            return m_buffer[l_*m_C + c_];
        }
        real & operator()(size_t l_, size_t c_) {
            assert(l_ < m_L);
            assert(c_ < m_C);
            return m_buffer[l_*m_C + c_];
        }
        void swap(NMatrix & other) {
            std::swap(m_buffer, other.m_buffer);
            std::swap(m_L, other.m_L);
            std::swap(m_C, other.m_C);
        }
        size_t L() const { return m_L; } // + C()
    private:
        // je reprends l'approche mono-alloc pour me simplifier la vie ici
        real * m_buffer;
        size_t m_L;
        size_t m_C;
    };
     
    struct VMatrix
    {
        typedef int real;
        VMatrix(std::size_t L_, std::size_t C_)
        : m_buffer(L_*C_), m_L(L_), m_C(C_)
        {
        }
        real const& operator()(size_t l_, size_t c_) const {
            assert(l_ < m_L);
            assert(c_ < m_C);
            return m_buffer[l_*m_C + c_];
        }
        real & operator()(size_t l_, size_t c_) {
            assert(l_ < m_L);
            assert(c_ < m_C);
            return m_buffer[l_*m_C + c_];
        }
        size_t L() const { return m_L; } // + C()
    private:
        std::vector<real> m_buffer;
        size_t m_L;
        size_t m_C;
    };
     
    template <typename Tab> void write(Tab& t_, char const* msg_){
        // Un premier accès pour mettre en cache
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                t_[y][x] = x*y;
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    t_[y][x] = x*y;
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
    }
     
    template <typename Tab> void Mwrite(Tab& t_, char const* msg_){
        // Un premier accès pour mettre en cache
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                t_(y,x) = x*y;
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    t_(y,x) = x*y;
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
    }
     
    template <typename Tab> int read(Tab const& t_, char const* msg_){
        // Un premier accès pour mettre en cache
        int v;
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                v += t_[y][x];
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    v += t_[y][x];
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
        return v;
    }
     
    template <typename Tab> int Mread(Tab const& t_, char const* msg_){
        // Un premier accès pour mettre en cache
        int v;
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                v += t_(y,x);
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    v += t_(y,x);
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
        return v;
    }
     
    template <typename Tab> void transpose(Tab & t_, char const* msg_)
    {
        // Un premier accès pour mettre en cache
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                t_[y][x] = t_[x][y];
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    t_[y][x] = t_[x][y];
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
    }
     
     
    template <typename Tab> void Mtranspose(Tab & t_, char const* msg_)
    {
        // Un premier accès pour mettre en cache
        ftime(&start);
        for (int y = 0; y < nYLoop; y++)
            for (int x = 0; x < nXLoop; x++)
                t_(y,x) = t_(x,y);
        endTimer();
        // Moyenne
        double sum_delta = 0;
        for (std::size_t k=0;k!=K;++k) {
            ftime(&start);
            for (int y = 0; y < nYLoop; y++)
                for (int x = 0; x < nXLoop; x++)
                    t_(y,x) = t_(x,y);
            endTimer();
            sum_delta+=delta;
        }
        std::cout << msg_ <<  delta << " -- moy: " << sum_delta/K << "\n";;
    }
     
     
    int main()
    {
        int nData;
     
        ///////////////////////////
        //////  Creation  /////////
        ///////////////////////////
     
        std::cout << "\n   CREATE \n\n";
     
        ftime(&start);
        std::vector< std::vector<int> > vec(nXLoop, std::vector<int>(nYLoop));
        endTimer();
        std::cout << "Vector : "<<  delta << "\n";
     
        ftime(&start);
        int** aNew(new int*[nYLoop]);
        for (int i = 0; i < nYLoop; i++){
            aNew[i] = new int[nXLoop];
        }
        endTimer();
        std::cout << "ArrayNew : "<<  delta << "\n";
     
     
        ftime(&start);
        int** aMalloc;
        aMalloc = (int**) malloc(nYLoop*sizeof(int*));
        for (int i = 0; i < nYLoop; i++){
            aMalloc[i] = (int*) malloc(nXLoop*sizeof(int));
        }
        endTimer();
        std::cout << "ArrayMalloc pas robuste : "<<  delta << "\n";;
     
        {
            ftime(&start);
            int** aMalloc = static_cast<int**>(malloc(nYLoop*sizeof(int*)));
            if (!aMalloc) throw std::bad_alloc();
            int i=0;
            for (; i < nYLoop; i++){
                aMalloc[i] = static_cast <int*>(malloc(nXLoop*sizeof(int)));
                if (!aMalloc[i]) break;
            }
            if (i != nYLoop) {
                for (size_t j=0;j!=i;++j) {
                    free(aMalloc[j]);
                }
                free(aMalloc);
                throw std::bad_alloc();
            }
            endTimer();
            std::cout << "ArrayMalloc robuste : "<<  delta << "\n";;
        }
     
        ftime(&start);
        int** aCalloc;
        aCalloc = (int**) calloc(nYLoop,sizeof(int*));
        if (!aCalloc) throw std::bad_alloc();
        for (int i = 0; i < nYLoop; i++){
            aCalloc[i] = (int*) calloc(nXLoop,sizeof(int));
            if (!aCalloc[i]) throw std::bad_alloc();
        }
        endTimer();
        std::cout << "ArrayCalloc quasi-robuste : "<<  delta << "\n";;
     
        ftime(&start);
        NMatrix nm0(nYLoop, nXLoop);
        endTimer();
        std::cout << "NewMatrix-0: "<<  delta << "\n";;
     
        ftime(&start);
        NMatrix nm_(nYLoop, nXLoop, false);
        endTimer();
        std::cout << "NewMatrix-?: "<<  delta << "\n";;
     
        ftime(&start);
        VMatrix vm(nYLoop, nXLoop);
        endTimer();
        std::cout << "VectMatrix : "<<  delta << "\n";;
     
     
        /////////////////////////////////////////
        ////////////////Write/////////////////////
        /////////////////////////////////////////
        std::cout << "\n   WRITE \n\n";
     
     
        write(vec    , "vector     : "); 
        write(aNew   , "ArrayNew   : "); 
        write(aMalloc, "ArrayMalloc: "); 
        write(aCalloc, "ArrayCalloc: "); 
        Mwrite(nm0    , "NewMatrix-0: "); 
        Mwrite(nm_    , "NewMatrix-?: "); 
        Mwrite(vm     , "VectMatrix : "); 
     
     
        /////////////////////////////////////////
        ////////////////Read/////////////////////
        /////////////////////////////////////////
     
        const std::size_t K = 1;
        std::cout << "\n   READ \n\n";
     
        int v;
        v= read(vec    , "vector     : "); 
        v+= read(aNew   , "ArrayNew   : "); 
        v+= read(aMalloc, "ArrayMalloc: "); 
        v+= read(aCalloc, "ArrayCalloc: "); 
        v+= Mread(nm0    , "NewMatrix-0: "); 
        v+= Mread(nm_    , "NewMatrix-?: "); 
        v+= Mread(vm     , "VectMatrix : "); 
        std::cerr << v << "\n";
     
        /////////////////////////////////////////
        ////////////////Both/////////////////////
        /////////////////////////////////////////
     
        std::cout << "\n   Transpose (R+W) \n\n";
     
        transpose(vec    , "vector     : "); 
        transpose(aNew   , "ArrayNew   : "); 
        transpose(aMalloc, "ArrayMalloc: "); 
        transpose(aCalloc, "ArrayCalloc: "); 
        Mtranspose(nm0    , "NewMatrix-0: "); 
        Mtranspose(nm_    , "NewMatrix-?: "); 
        Mtranspose(vm     , "VectMatrix : "); 
    }
    // Vim: let $CXXFLAGS='-O3 -std=c++0x -DNDEBUG'
    J'obtiens:
    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
    $ ./perf_vect 
     
       CREATE 
     
    Vector : 0.188
    ArrayNew : 0.025
    ArrayMalloc pas robuste : 0.025
    ArrayMalloc robuste : 0.022
    ArrayCalloc robuste : 0.153
    NewMatrix-0: 0.22
    NewMatrix-?: 0
    VectMatrix : 0.212
     
       WRITE 
     
    vector     : 0.111 -- moy: 0.1106
    ArrayNew   : 0.111 -- moy: 0.1106
    ArrayMalloc: 0.11 -- moy: 0.1106
    ArrayCalloc: 0.113 -- moy: 0.112
    NewMatrix-0: 0.111 -- moy: 0.1116
    NewMatrix-?: 0.112 -- moy: 0.113
    VectMatrix : 0.111 -- moy: 0.1106
     
       READ 
     
    vector     : 0.085 -- moy: 0.0854
    ArrayNew   : 0.086 -- moy: 0.0856
    ArrayMalloc: 0.086 -- moy: 0.0856
    ArrayCalloc: 0.086 -- moy: 0.0856
    NewMatrix-0: 0.085 -- moy: 0.0852
    NewMatrix-?: 0.086 -- moy: 0.0856
    VectMatrix : 0.085 -- moy: 0.0854
    320083016
     
       Transpose (R+W) 
     
    vector     : 0.69 -- moy: 0.6938
    ArrayNew   : 0.675 -- moy: 0.6754
    ArrayMalloc: 0.675 -- moy: 0.6754
    ArrayCalloc: 0.676 -- moy: 0.6756
    NewMatrix-0: 0.699 -- moy: 0.6998
    NewMatrix-?: 0.699 -- moy: 0.7
    VectMatrix : 0.699 -- moy: 0.6998
    On voit que la 0-init coute, mais d'un autre côté, elle charge la zone de mémoire pour permettre d'y accéder par la suite. Mais vu que ce qui compte ce sont les accès en R/W... ce n'est pas très grave.
    NB: j'avais caché les 1ers accès, mais ils sont intéressants à regarder.
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  6. #6
    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
    Je suis un peu surpris de l’accueil critique de ce post, d'autant plus que je suis absolument d'accord avec vos réponses...

    - Oui le test en debug n'indique strictement rien d'ailleurs c'est bien ce que je dis dans mon post. D'ailleurs le test lui même n'indique pas grand chose (vu que la machine peut avoir d'autres occupation au même moment et ralentir)
    - Oui la range based loop a un code différent (Le fait de devoir accéder au contenu précédent me complique la vie)
    - Oui écrire du code imbittable sous pretexte de vouloir optimiser dès le début est une mauvaise idée, ca fait souvent des codes plus lents et moins stables.
    - Non j'ai pas cherché à prouver que std::vector était lent d'ailleurs ce test montre (dans la limite de ce qu'il vaut) qu'une fois la compilation optimisée lancée 3 bout de codes écrit à l'arrache pour faire la même chose ont des perfs comparables

    Bref c'était un petit test histoire d'avoir une meilleure idée des perfs des différentes approches.

    Donc oui, tes barbus ont raisons : un code C with class mal écrit sera plus rapide. Sur le court terme. Parce que a long terme, tu passeras beaucoup plus de temps a debuger tes codes mal écrit. Parce que c'est typiquement le genre de problématiques qui produisent des comportements indéterminés, parfois très compliqué a debuger (non reproductible, plantage qui arrive de temps en temps, mais qui est critique) et que l'on peut traîner pendant des jours, voire des semaines.
    Pour avoir déjà chassé le Heisenbug je ne peux qu'être d'accord avec ça. Ce sont les arguments pour convaincre les barbus maniaque de leurs vitesses (et parfois à tord) qui me manque.

  7. #7
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Desole si le ton était un peut trop "impulsif", on a trop l'habitude de gens qui essaient de convaincre que le C++ est moins bien que le C, avec des codes de benchmarks foireux.

    Citation Envoyé par _zzyx_ Voir le message
    Ce sont les arguments pour convaincre les barbus maniaque de leurs vitesses (et parfois à tord) qui me manque.
    Laisse tomber, tu n'y arriveras pas

    Dans une autre discussion, Luc dit que le C++ "moderne", c'est l'utilisation du RAII. Je crois que c'est plus profond que cela. Le C++ moderne, c'est une façon différente de penser la conception d'application. En particulier penser la conception d'application a long terme (et donc ne pas forcement se focaliser sur le hack qui va nous faire gagner 1% de performances, mais poser pleins de problèmes de débogage et d’évolution du code). Le conséquence est de penser différemment la gestion de la mémoire et des erreurs et donc penser en termes de RAII.

    Tes "vieux barbus"... (je n'aime pas cette expression, a quel moment on est un "vieux barbus" ? A un age precis ? Ou lorsque l'on a décidé de se reposer sur ses connaissances acquises, sans se renouveler ? Il est possible que je fasse parti des "vieux barbus" dans le premier cas - et je pense que c'est le cas pour Luc aussi. Par contre, je n'entre pas - j’espère - dans la seconde catégorie. Fin du HS)
    Tes "vieux barbus" sont probablement convaincu de leur approche. Vouloir les convaincre, c'est vouloir lancer une discussion "C++ vs Java" sans troll : c'est impossible.

    Si tu as la possibilité, fais ton code comme tu le sens. Si tu es oblige de suivre tes "vieux barbus"... fais au mieux. Ca te feras une expérience sur ce qu'il ne faut pas faire

  8. #8
    r0d
    r0d est déconnecté
    Expert éminent

    Homme Profil pro
    tech lead c++ linux
    Inscrit en
    Août 2004
    Messages
    4 262
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : tech lead c++ linux

    Informations forums :
    Inscription : Août 2004
    Messages : 4 262
    Points : 6 680
    Points
    6 680
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Dans une autre discussion, Luc dit que le C++ "moderne", c'est l'utilisation du RAII. Je crois que c'est plus profond que cela. Le C++ moderne, c'est une façon différente de penser la conception d'application. En particulier penser la conception d'application a long terme (et donc ne pas forcement se focaliser sur le hack qui va nous faire gagner 1% de performances, mais poser pleins de problèmes de débogage et d’évolution du code). Le conséquence est de penser différemment la gestion de la mémoire et des erreurs et donc penser en termes de RAII.
    C'est toute la difficulté de ces appellations. Pour certains, lorsque qu'on parle de "c++ moderne", ils voient tout de suite le "modern c++ design" d'Alexandrescu et donc pour eux, c++ moderne == template.

    Citation Envoyé par Andrei Alexandrescu
    Modern C++ Design defines and systematically uses generic components - highly flexible design artifacts that are mixable and matchable to obtain rich behaviors with a small, orthogonal body of code.
    Ce que je veux dire c'est que c'est pas évident d'utiliser ce type de formule "c++ moderne, c++ contemporain", parce qu'elles ont un sens différent pour chacun. Je suis bien conscient que c'est une aporie, mais à la limite je préfère utiliser une référence au standard (c++98, c++03, c++11, c++14, ...).

    Citation Envoyé par mintho carmo Voir le message
    Vouloir les convaincre, c'est vouloir lancer une discussion "C++ vs Java" sans troll : c'est impossible.
    Je dois être particulièrement chanceux alors, parce que dans ma boite c'est possible, et nous le faisons quotidiennement. Le contexte est peut-être singulier: nous sommes une dizaine de développeurs, répartis en trois équipes, une équipe java, une équipe c# et une équipe c++. Non seulement nous discutons régulièrement et sans troller des différences entre chaque langage, mais en plus nous tournons régulièrement (enfin pas tous non plus, seuls ceux qui veulent). Moi par exemple, je viens de passer environ 6 mois sur du code java, et en ce moment je suis sur de l'optimisation de code c#, alors que je suis le lead dev de la team c++.
    Enfin bref, tout ça pour dire que si, c'est possible! C'est probablement rare, mais ça existe, il ne faut pas perdre espoir
    « L'effort par lequel toute chose tend à persévérer dans son être n'est rien de plus que l'essence actuelle de cette chose. »
    Spinoza — Éthique III, Proposition VII

  9. #9
    Membre éclairé

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    393
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 393
    Points : 685
    Points
    685
    Par défaut
    Je crois que l'on est tous conscient de la difficulté de définir "moderne" et c'est bien pour cela que l'on utilise souvent les guillemets
    Je pense que je suis proche de l'avis de Luc, dans le sens ou je trouve qu'il y a encore beaucoup de personnes qui pensent le C++ comme ils pensent le C (ou tout au moins un mix bâtard de C et C++, voire d'autres langages). L'utilisation d'une syntaxe particulière est dans ce cas anecdotique, le principal objectif "pédagogique" est surtout d'expliquer que ce n'est pas une perte de temps de s’intéresser a comment on peut faire évoluer les choses.

    Pour le C++ vs Java, je te rassure, cela m'est arrivé d'avoir des discussions sérieuses la dessus. Mais j'ai abandonné l’idée que cela soit possible sur un forum "grand public" comme Dvp.
    Et je crois que c'est aussi une perte de temps de vouloir convaincre une personne qui est fermée sur ses idées et ne veut pas évoluer. Pas plus tard qu'hier, j'ai passé 2 heures a discuter avec une personne qui avait parfaitement conscience que ce qu'il faisait était de mauvaises pratiques, mais que du moment que cela compile, ça lui convient (destructeur public non virtuel dans une hiérarchie de classe, héritage multiple en diamant dans héritage virtuel, dangling pointeurs dans tous les sens). Je passe mon temps dans mon projet actuel a chasser les erreurs de conception, les UB, les Heisenbug, etc. a cause de ce genre de conn... Juste parce que certains ne savent pas évoluer.

Discussions similaires

  1. Vecteur de tableau à 2 dimensions
    Par Gregoire31 dans le forum C++
    Réponses: 7
    Dernier message: 19/12/2011, 15h01
  2. Tableau XML - test existance de ligne suivante
    Par Alaster dans le forum iReport
    Réponses: 3
    Dernier message: 10/12/2009, 12h01
  3. Transformer un vecteur en tableau de float
    Par DiverSIG dans le forum Collection et Stream
    Réponses: 3
    Dernier message: 19/11/2008, 17h41
  4. Quelques tests sur une FIFO
    Par mrttlemonde dans le forum Linux
    Réponses: 2
    Dernier message: 07/06/2006, 17h52

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