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 :

Le draft de la prochaine norme C++14 est disponible !


Sujet :

C++

  1. #61
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    @Klaim: Tu pourrais donner une référence, un lien, un article, une discussion ? Je suppose qu'il ne fait pas que dire "c'est coûteux" mais qu'il explique le pourquoi, ce qui m’intéresse.
    Rien de precis mais si tu n'as pas encore regarde les sessions C++ and Beyond sur le multi-threading, ca se passe la si ma memoire est bonne (et de toutes facons ca sera interessant).


    Globalement le cout est celui dont on parle: deux atomiques a gerer, un pour les weak_ptr et l'autre pour les shared_ptr. Note que l'atomic a beau etre lockfree, comme je disais, il y a des cas ou tu vas voir les perfs, mais faut avoir beaucoup d'objets et faire pas mal de partage qui changent frequement. Tout cela compare a des pointeurs libres evidemment.

    J'imagine qu'il est possible de demontrer rapidement le cout via un benchmark mais personnellement je n'ai pas le temps d'en mettre un en place.

  2. #62
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    Citation Envoyé par Flob90 Voir le message
    @Klaim: Tu pourrais donner une référence, un lien, un article, une discussion ? Je suppose qu'il ne fait pas que dire "c'est coûteux" mais qu'il explique le pourquoi, ce qui m’intéresse.
    Herb Sutter a fait un article expliquant le souci de perf potentiel avec shared_ptr, sans fournir de mesure evidemment mais on sait bien qu'une operation atomique est jamais aussi rapide qu'une operation non-atomique : http://herbsutter.com/2013/06/05/got...er-parameters/

    What’s so bad about a “shared reference count increment and decrement?” Two things, one related to the “shared reference count” and one related to the “increment and decrement.” Fortunately it’s not all that bad usually, which is why smart pointers are feasible for widespread use. However, it’s good to be aware of how this can incur performance costs for two reasons: one major and common, and one less likely in well-designed code (which the above, alas, is not) and so probably more minor.

    First, the major reason is the performance cost of the “increment and decrement”: Because the reference count is an atomic shared variable (or equivalent), incrementing and decrementing it are internally-synchronized read-modify-write shared operations. Briefly, omitting some subtle detailed analysis, the good news is that the function entry is usually quite cheap because incrementing the smart pointer reference count can usually be optimized to be the same as an ordinary increment in an optimized shared_ptr implementation—just an ordinary increment instruction, and no fences, in the generated code. However, the decrement must be an atomic decrement or equivalent, which generates special processor memory instructions that are more expensive in themselves, and that on top of that induce memory fence restrictions on optimizing the surrounding code.

  3. #63
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Qu'il y est un coût, je suis totalement d'accord, par contre les bench montrent plusieurs choses (résultat du bench et code en dessous) :
    • Il est extrêmement faible. Sans compter que la situation de la mesure est une situation qui ne peut pas se produire : copier/supprimer des millions de pointeur intelligents à la suite n'arrivera jamais à moins de le faire exprès. Il y a en général des instructions entre, et donc l'impact de cette mesure se retrouve étalé sur des temps grands devant la baisse de performance. Pour la mesurer il faudrait quantifier un nombre moyen de copies/suppressions de pointeur intelligent sur un temps donnée.
    • Contrairement à ce que je pensais, le coût entre MT et single est aussi important qu'entre le single et le raw.
    • Contrairement à ce qu'annonce H.Sutter, il ne semble pas avoir d'optimisation qui favorise les performances des copies et fait défaut à la destruction entre le MT et le single. On note même que la destruction est plus rapide que la copie dans tout les cas.


    Détail du bench :
    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
     
    #include<ctime>
    #include<type_traits>
    #include<memory>
    #include<utility>
    #include<vector>
     
    //#define BOOST_SP_DISABLE_THREADS //Switch between single thread and lock free
     
    #include<boost\smart_ptr.hpp>
     
    template<class I, class F, class... Arg>
    double bench(F&& f, Arg&&... arg)
    {
        using value_type = typename I::value_type;
     
        auto begin(clock());
        for(auto i = value_type(); i<I::value; ++i)
            f(std::forward<Arg>(arg)...);
        auto end(clock());
        return double(end-begin)/CLOCKS_PER_SEC;
    }
     
    template<size_t T>
        using size_constant = std::integral_constant<size_t,T>;
     
    int main(){
        using size = size_constant<100000000>;
    //    using containor_ptr = std::unique_ptr<std::vector<boost::shared_ptr<int>>>; //bench shared_ptr
    //    using containor_ptr = std::unique_ptr<std::vector<int*>>;                   //bench raw ptr
     
    //    auto p(boost::make_shared<int>(0)); //bench shared_ptr
    //    auto p(new int(0));                 //bench raw
        containor_ptr v(new containor_ptr::element_type());
        v->reserve(size::value);
       std::cout << bench<size>(
            [&v,&p](){v->push_back(p);}
        ) << std::endl;
    /*    std::cout << bench<size_constant<1>>(
            [](containor_ptr){}
        ,std::move(v)) << std::endl;*/ //bench shared_ptr
    /*    std::cout << bench<size_constant<1>>(
            [&p](containor_ptr){delete p;}
        ,std::move(v)) << std::endl;*/ //bench raw ptr
    }
    Machine : Intel Core i3 2.1 GHz
    OS : W7 SP1
    Compilateur : MinGW 4.7.1 (tdm 64)

    Résultats :

    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
     
                      Operation
     
                      || copy   | remove || copy    | remove
                      ||        |        ||         |
                      ||        |        ||         |
    Pointer           ||        |        ||         |
                      ||        |        ||         |
    raw               || 0.718  | 0.090  || 0.746   | 0.090
    lock-free         || 1.963  | 1.569  || 2.135   | 1.559
    single thread     || 1.248  | 0.768  || 1.329   | 0.724
                      ||                 ||
                      || O2              || Os
                      ||
                      ||  Optimisation
    Il y a deux mesures pour chaque tests :
    • La première est la mesure de l'insertion par copie de 100 millions de pointeurs dans une conteneurs. Le choix est un std::vector pour ne pas avoir l'impact dans la non-localité dans la mesure de la copie, la taille est réservée avant pour ne pas la prendre en compte. L'ordre de grandeurs des temps (1~2s) comparé à la quantité d'éléments copiés en une fois vient appuyer mon premier point. En effet on peut aisément imaginer qu'un tel quantité de copie soit étallé sur une durée de programme assez grand devant ce temps typique de 1~2s.
    • La seconde est la mesure de la suppression de ce conteneur (par move semantic, il y a donc probablement le coût de quelques copies de pointeur, ceux interne au conteneur, en plus).

    Les deux mesures sont donc bien l'image de ce qu'évoque H.Sutter : les coûts d'incrémentation et de décrémentation. Et la comparaison avec des raw ptr permet d'avoir une idée du coût du compteur en lui-même.

    NB: En relatif, ces mesures indiquent des baisses de performances significatives, cependant il faut aussi les voir en absolue et les mettre en regard avec la durée du programme dans lequel autant d'instruction de ce type ont eu lieu.

    PS: Ça ne met pas en cause les conclusions d'H.Sutter, je suis totalement d'accord (j'ai pas lu en détail mais ces guideline me conviennent aussi à priori), mais plutôt pour la signification qu'on les différentes façon de faire que pour d'éventuelles gain de performances.

    Edit: J'ai voulu dessiner un tableau pour les résultats, mais ca ne donne pas ce que je veux. En copiant/collant dans le bloc note on retrouve la forme assez facilement. Merci JolyLoic, je n'avais pas pensé à utiliser la balise code pour ça.

  4. #64
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    C'est marrant, en testant avec std::shared_ptr (g++-4.7.3) plutôt que boost::shared_ptr + BOOST_SP_DISABLE_THREADS j’obtiens de meilleur perf (+ de 30% ).
    Par contre, pas de différence en MT et en compilant avec -pthread.

  5. #65
    En attente de confirmation mail

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2004
    Messages
    1 391
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : France, Doubs (Franche Comté)

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

    Informations forums :
    Inscription : Août 2004
    Messages : 1 391
    Points : 3 311
    Points
    3 311
    Par défaut
    Tu as bench avec quel code exactement ? Parce que je n'obtiens pas ces résultats chez moi :

    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
                      Operation
     
                      || copy   | remove
                      ||        |
                      ||        |
    Pointer           ||        |
                      ||        |
    raw               || 0.616  | 0.081
    lock-free         || 1.950  | 1.442
    single thread     || 0.951  | 0.718
    std               || 2.085  | 1.629
                      ||
                      || O2
                      ||
                      || Optimisation
    Donc pas de différence bien significative entre std et boost.

    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
    #include<ctime>
    #include<type_traits>
    #include<memory>
    #include<utility>
    #include<vector>
     
    //#define BOOST_SP_DISABLE_THREADS //Switch between single thread and lock free
     
    #include<boost\smart_ptr.hpp>
    #include<memory>
     
    template<class I, class F, class... Arg>
    double bench(F&& f, Arg&&... arg)
    {
        using value_type = typename I::value_type;
     
        auto begin(clock());
        for(auto i = value_type(); i<I::value; ++i)
            f(std::forward<Arg>(arg)...);
        auto end(clock());
        return double(end-begin)/CLOCKS_PER_SEC;
    }
     
    template<size_t T>
        using size_constant = std::integral_constant<size_t,T>;
     
    int main(){
        using size = size_constant<100000000>;
     
    //    namespace N = std;   //bench std
    //    namespace N = boost; //bench boost
     
    //    using containor_ptr = std::unique_ptr<std::vector<N::shared_ptr<int>>>; //bench shared_ptr
    //    using containor_ptr = std::unique_ptr<std::vector<int*>>;               //bench raw ptr
     
    //    auto p(N::make_shared<int>(0)); //bench shared_ptr
    //    auto p(new int(0));             //bench raw
        containor_ptr v(new containor_ptr::element_type());
        v->reserve(size::value);
       std::cout << bench<size>(
            [&v,&p](){v->push_back(p);}
        ) << std::endl;
     
    /*    std::cout << bench<size_constant<1>>(
            [](containor_ptr){}
        ,std::move(v)) << std::endl;*/ //bench shared_ptr
    /*    std::cout << bench<size_constant<1>>(
            [&p](containor_ptr){delete p;}
        ,std::move(v)) << std::endl;*/ //bench raw ptr
    }

  6. #66
    Membre expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    739
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

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

    Informations forums :
    Inscription : Juin 2011
    Messages : 739
    Points : 3 627
    Points
    3 627
    Par défaut
    Je me suis trompé de version de compilo (j'utilise un alias pour compiler '^^).
    Donc en fait, c'est g++-4.8. Et effectivement, les résultats sont identique avec g++-4.7.
    g++4-8 ça roxx

Discussions similaires

  1. la norme C90 - Elle est ou?
    Par wee_bee dans le forum C
    Réponses: 3
    Dernier message: 17/06/2010, 00h01
  2. obtenir le prochain ID en mode auto-increment
    Par arnololo dans le forum MS SQL Server
    Réponses: 3
    Dernier message: 03/12/2003, 18h43
  3. wxWindows et DevC++ : taille de l'exe énorme !
    Par ovh dans le forum Dev-C++
    Réponses: 7
    Dernier message: 19/11/2003, 18h01
  4. Normes EDI
    Par f-demu01 dans le forum Langages de programmation
    Réponses: 2
    Dernier message: 14/03/2003, 09h22

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