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 :

Chronometrer execution fonction générique


Sujet :

C++

  1. #1
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut Chronometrer execution fonction générique
    Bonjour,

    Je cherche à créer une classe permettant de chronométrer le temps d’exécution d'une fonction C (ou C++). Le problème c'est que j'aimerais un chronomètre générique qui s'adapte à n'importe quel prototype de fonction :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    void fct1(int a);
    int* fct2();
    void fct3(void* param);
    Le but serait de faire un truc du genre :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    MonChrono.Bench(fct1); 
    MonChrono.Bench(fct2); 
    MonChrono.Bench(fct3);
    Je maitrise les pointeurs de fonctions mais je ne sais pas du tout comment m'y prendre...

    Par contre, je ne veux pas me limiter en disant : "seules les fonctions ne retournant rien (void) et prenant un seul paramètre générique en entrée pourront être benchées"

    J'aimerais que cela marche pour n'importe quel type de fonctions.

    Voilà, ca se trouve ce n'est même pas possible mais je me dis qu'avec tout ce que offre le C++, il y a peut être une solution

    Merci à tous
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

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

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Pour le chrono lui-même tu peux regarder du coté de C++11
    http://aubedesheros.blogspot.fr/2010...onometres.html

    Pour passer ta fonction, tu peux utiliser la programmation générique avec un template.
    Par exemple ce code (non testé) peut prendre:
    - une fonction sans paramètre
    - un objet foncteur
    - une lambda expression sans paramètre
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    #include <chrono>
     
    template <class func>
    double bench(func & f)
    {
        auto t_start = std::chrono::high_resolution_clock::now();
        f(); // On exécute la fonction ou foncteur ou lambda
        auto t_end = std::chrono::high_resolution_clock::now();
        return std::chrono::duration<double>(t_end - t_start).count(); // Retourne le temps en secondes
    };
    Ensuite si tu veux passer des arguments ou recupérer le retour de la fonction, tu peux le "simuler" avec un objet foncteur ou une lambda expression.
    Voici un exemple (non testé) de foncteur qui permet d'appeler une fonction addition (supposée existante) qui prend deux entiers i, j et "retourne" un entier.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Foncteur
    {
    public:
        Fonteur(int i, int j) : m_i(i), m_j(j) { }
        void operator()() { m_result = addition(m_i, m_j); }
        int getResult() const { return m_result; }
    private:
        int m_i;
        int m_j;
        int m_result;
    }
    PS: Grosso modo: un foncteur est objet qui possède un opérateur () et qui peut (comme son nom l'indique) être utilisé comme une fonction.

    L'appel de la fonction simple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    int r = addition(21, 42);
    Avec le bench:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Foncteur f(21, 42);
    double time = bench(f);
    int r = f.getResult();
    (non testé également)

    Donc ça ne marche pas «pour n'importe quel type de fonctions» mais il est facile de créer un foncteur ou une lambda expression qui encapsule la fonction que l'on veut bencher.

    --- --- ---

    Pour mes "benchs" (simple mesures de temps) je me suis créé une classe qui a un start() et un stop() et qui enregistre les temps associés à un identifiant (en utilisant std::map).
    C'est très simple à implémenter et ça s'utilise comme ceci:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     hnc::benchmark b;
    b["Name of the test"].start();
    // Some computation
    b["Name of the test"].stop();
    // Voilà comment recupérer le temps (il est possible d'avoir plusieurs start/stop par identifiant):
    double min    = b["Name of the test"].min();
    double max    = b["Name of the test"].max();
    double mean   = b["Name of the test"].mean();
    double median = b["Name of the test"].median();
    std::vector<double> times_elapsed = b["Name of the test"].all();

  3. #3
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Salut,

    Donc cela veut dire que pour chaque fonction, il faut lui créer un foncteur associé en fonction de ses paramètres et de ce qu'elle retourne. Ce n'est pas un peu lourd ?

    Sinon, je n'ai pas bien compris ton implémentation avec des std::map, pourrais tu me donner ta classe si cela ne te dérange pas ? Le cas échéant, m'expliquer plus en détail ton implémentation ?

    Sinon, ta méthode fonctionne dans sa globalité
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

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

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Donc cela veut dire que pour chaque fonction, il faut lui créer un foncteur associé en fonction de ses paramètres et de ce qu'elle retourne. Ce n'est pas un peu lourd ?
    Issu de la FAQ C++ http://cpp.developpez.com/faq/cpp/?page=STL#STL_functor
    «Notez qu'on peut tout à fait utiliser des pointeurs sur fonction au lieu de foncteurs (il suffit de pouvoir appliquer l'opérateur () à l'objet passé en paramètre), mais on préfèrera les foncteurs car ils offrent plus de performances et de flexibilité. En effet operator() peut être entièrement inliné, et on peut passer des paramètres au foncteur via son constructeur pour plus de souplesse.»

    Citation Envoyé par Aspic Voir le message
    Sinon, je n'ai pas bien compris ton implémentation avec des std::map, pourrais tu me donner ta classe si cela ne te dérange pas ? Le cas échéant, m'expliquer plus en détail ton implémentation ?
    Je n'ai pas copié le code de la moyenne et de la médiane et ai supprimé une partie (redondante) de la doxygen.
    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
    #include <chrono>
    #include <vector>
    #include <map>
    #include <string>
    #include <iostream>
    #include <algorithm>
     
    namespace hnc
    {
    	/**
             * @brief Vector of elapsed times
             *
             * @warning hnc::benchmark_base is just a class to stock benchmark data @n
             * Please consider hnc::benchmark and hnc::benchmark_name_opt
             *
             * hnc::benchmark_base is a vector (std::vector) of elapsed times between start and stop
             * You can access to the min, max, mean, median and all elapsed times
             */
    	class benchmark_base
    	{
    	private:
     
    		/// Vector of elapsed times
    		std::vector<double> m_times;
     
    		/// Temporary time point to get duration between start and end
    		std::chrono::time_point<std::chrono::high_resolution_clock> m_tmp_time_point;
     
    	public:
     
    		/// Start timer for benchmark
    		void start()
    		{
    			m_tmp_time_point = std::chrono::high_resolution_clock::now();
    		}
     
    		/// Stop timer and save the duration (elapsed time during last .start()) (in seconds)
    		void stop()
    		{
    			m_times.push_back(std::chrono::duration<double>(std::chrono::high_resolution_clock::now() - m_tmp_time_point).count());
    		}
     
    		/**
                     * Add (push back) a user elapsed time value
                     * @param[in] value value of one elapsed time
                     */
    		void push_back(double const value) { m_times.push_back(value); }
     
    		/// @return the minimum of elapsed time
    		double min() const { return *std::min_element(m_times.begin(), m_times.end()); }
     
    		/// @return the maximum of elapsed time
    		double max() const { return *std::max_element(m_times.begin(), m_times.end()); }
     
    		/// @return the mean of all elapsed times
    		double mean() const { return 0.; /*hnc::math::mean(m_times);*/ }
     
    		/// @return the median of all elapsed times
    		double median() const { return 0.; /*hnc::math::median(m_times);*/ }
     
    		/// @return a vector of all elapsed times
    		std::vector<double> const & all() const { return m_times; }
    	};
    }
    Ca permet de faire une mesure brute.

    Souvent, on nomme les benchs donc on peut faire ce genre de typedef:
    Par contre /!\ on mesure également le temps de recherche du benchmark_base de std::map lors du .stop() !
    (On peut toujours créer un benchmark_base par bench et ajouter les mesures de temps manuellement avec .push_back(double const value))
    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
    namespace hnc
    {
    	/**
             * @brief Save benchmarks by name
             *
             * hnc::benchmark is a map (std::map) of hnc::benchmark_base @n
             * The name of the test (a std::string) is the key and the hnc::benchmark_base is the value
             *
             * The value is a vector of elapsed times (in seconds) @n
             * To get a elapsed time use the start and stop method
             *
             * You can acces to some metrics:
             * - min
             * - max
             * - mean
             * - median
             * - all elapsed times
             *
             * @code
             * hnc::benchmark b;
             *
             * b["Name of the test"].start();
             * // Some computation
             * b["Name of the test"].stop();
             *
             * // You can add user elapsed time value (a double)
             * b["Name of the test"].push_back(5.);
             *
             * double min    = b["Name of the test"].min();
             * double max    = b["Name of the test"].max();
             * double mean   = b["Name of the test"].mean();
             * double median = b["Name of the test"].median();
             * std::vector<double> times_elapsed = b["Name of the test"].all();
             * @endcode
             */
    	typedef std::map<std::string, hnc::benchmark_base> benchmark;
    }
    J'ai aussi un autre typedef. Je m'en sers pour mesurer le temps d'un code ou d'une fonction dans différents contextes. Par exemple dans le cadre de mesure de temps d'une fonction parallélisée en faisant varier le nombre de cores utilisés. (Ensuite j'ai des classes qui créent des script gnuplot et qui prennent souvent des std::map<std::string, std::map<std::string, T>> (la conversion est facile et les clefs servent pour les labels)).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef std::map<std::string, std::map<std::string, hnc::benchmark_base>> benchmark_name_opt;
    Citation Envoyé par Aspic Voir le message
    Sinon, ta méthode fonctionne dans sa globalité

  5. #5
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Merci pour ton aide
    Je vais essayer de regarder ca quand j'aurais un peu de temps

    N'empêche, je maintiens que créer un foncteur pour chaque fonction que l'on veut bencher est lourd ^^

    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  6. #6
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Salut,
    Citation Envoyé par Aspic Voir le message
    Merci pour ton aide
    Je vais essayer de regarder ca quand j'aurais un peu de temps

    N'empêche, je maintiens que créer un foncteur pour chaque fonction que l'on veut bencher est lourd ^^

    Pas beaucoup plus lourd que de créer un pointeur de fonction, mais sommes toutes beaucoup plus simple

    Et puis, avec un peu de chance, tu peux parfaitement partir sur un foncteur template en ne faisant varier que le nombre d'arguments
    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
    /* un foncteur qui ne prend aucun paramètre et ne renvoie rien */
    struct NoParamNoReturn
    {
        void operator()();
    }
    /* un autre qui renvoie quelque chose mais ne prend aucun paramètre */
    template <typename R>
    struct NoParamButReturn
    {
        R operator()();
    };
    /* un troisième qui renvoie quelque chose et prend 5 paramètres */
    template<typename R, typename One, typename Two, typename Three,
             typename Four, typename Five>
    struct FiveParamsReturn
    {
        R operator()(One one, Two two, Three three, Four four, Five five); 
    };
    Et il y aurait meme moyen de simplifier encore d'avantage avec les variadic tempalte
    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

  7. #7
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Salut,

    Et puis, avec un peu de chance, tu peux parfaitement partir sur un foncteur template en ne faisant varier que le nombre d'arguments
    Ca à l'air cool

    Mais alors comment ca s'utiliserait ? Tu pourrais me donner un exemple pour bencher une fonction qui retourne quelque chose et qui prends 2 params en entrée ?

    Actuellement ma fonction bench est comme ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    template <class func>
    double bench(func & f)
    {
        auto t_start = std::chrono::high_resolution_clock::now();
        f(); // On exécute la fonction ou foncteur ou lambda
        auto t_end = std::chrono::high_resolution_clock::now();
        //return std::chrono::duration<double>(t_end - t_start).count(); // Retourne le temps en secondes
        std::chrono::microseconds u = (t_end - t_start);
        return u.count(); // Retourne le temps en microsecondes
    }
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  8. #8
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    En fait, si tu peux utiliser C++11 et les variadic template, tu pourrais très bien créer deux foncteurs tout à fait génériques, l'un pour travailler avec des fonctions libres, l'autre pour travailler avec des fonctions membres.

    Pour les fonctions libres, il ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #include <functional>
    template<typename ReturnType, typename ... Args>
    struct FunctionCaller
    {
       typedef std::function<ReturnType(Args...)> callee;
       callee fun;
        FunctionCaller(callee fun):fun(fun){}
        ReturnType operator()(Args...args)const
        {
           //"Debug mode " : pour etre sur qu'on passe bien par ici
           // std::cout<<"Called free function with "<<sizeof...(Args)<<" parameter"<<std::endl;
            return fun(args... );
        }
    };
    et pourrait, par exemple, être utilisé avec des fonctions comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    void print(int i)
    {
        std::cout<<"just the int "<<i<<std::endl;
    }
    void printStringInt(std::string const & s, int i)
    {
        std::cout<<"the string "<<s <<" and the int "<<i<<std::endl;
    }
    int multiply(int i, int j)
    {
        return i * j;
    }
    sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main()
    {
        FunctionCaller<void,int > caller(print);
        FunctionCaller<void, std::string const &, int > call2(printStringInt);
        FunctionCaller<int, int, int > call3(multiply);
        caller(3);
        call2("Salut",5);
        std::cout<< call3(5,2);
        return 0;
    }
    voire sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    using OneArgNoReturn = FunctionCaller<void, int >;
    using StringIntNoReturn = FunctionCaller<void,std::string const &, int >;
    using TwoIntReturnInt = FunctionCaller<int, int, int >;
    int main()
    {
        OneArgNoReturn  caller(print);
        StringIntNoReturn call2(printStringInt);
        TwoIntReturnInt call3(multiply);
        caller(3);
        call2("Salut",5);
        std::cout<< call3(5,2);
        return 0;
    }
    et, pour les fonctions membres de classe, cela il ressemblerait à
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    template<typename ClassType, typename ReturnType, typename ... Args>
    struct MemberFunctionCaller
    {
        typedef std::function<ReturnType(ClassType&, Args...)> callee;
        callee fun;
        MemberFunctionCaller(callee fun):fun(fun){}
        ReturnType operator()(ClassType & c, Args...args)const
        {
           //"Debug mode " : pour etre sur qu'on passe bien par ici
           // std::cout<<"Called member function with "<<sizeof...(Args)<<" parameter"<<std::endl;
            return fun(c,args... );
        }
    };
    et pourrait etre utilisé avec une fonction comme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    struct MyStruct
    {
        void foo(){std::cout<<"foo()"<<std::endl;}
        int multiply(int i, int j){std::cout<<"multiply()"<<std::endl;
                                   return i*j;}
    };
    sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        MyStruct s;
        MemberFunctionCaller<MyStruct,void> scall(&MyStruct::foo);
        MemberFunctionCaller<MyStruct,int,int,int> scall2(&MyStruct::multiply);
        scall(s);
        std::cout<< scall2(s,5,2);
        return 0;
    }
    [EDIT]Sauf erreur de ma part, tu devrais d'ailleurs pouvoir créer un alias template pour simplifier l'écriture avec MemberFunctionCaller, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template <typename ClassName> 
        using MemberOneArgNoReturn = 
              MemberFunctionCaller<ClassName,void>;
    template <typename ClassName> 
        using MemberTwoIntReturnInt = 
              MemberFunctionCaller<ClassName, int, int, int >;
    et l'utiliser avec quelque chose proche de
    sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
        MyStruct s;
        MemberOneArgNoReturn < MyStruct > scall(&MyStruct::foo);
        MemberTwoIntReturnInt < MyStruct > scall2(&MyStruct::multiply);
        scall(s);
        std::cout<< scall2(s,5,2);
        return 0;
    }
    (voire utiliser un alias complètement spécialisé )
    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

  9. #9
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Whaou, génial les variadic template !!!

    Par contre, au niveau du chrono, ca risque de coincer :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     ReturnType operator()(Args...args) const
        {
           //"Debug mode " : pour etre sur qu'on passe bien par ici
            std::cout<<"Called free function with "<<sizeof...(Args)<<" parameter"<<std::endl;
     
            // start chrono here
            return fun(args... );
     
            // stop chrono impossible here....
        }
    En effet, on peux démarrer le crhono dès que la fonction est appelée mais par contre, on ne pourra pas l'arrêter après le return...

    J'ai donc essayé ca :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    ReturnType operator()(Args...args) const
        {
           //"Debug mode " : pour etre sur qu'on passe bien par ici
            std::cout<<"Called free function with "<<sizeof...(Args)<<" parameter"<<std::endl;
     
            // start chrono here
     
            ReturnType r = fun(args... );
     
            // stop chrono here
     
            return r;
        }
    Mais ca ne compile pas, normal si on passe un ReturnType de type void, ca plante (d'ailleurs même dans l'autre cas, ca devrait planter pour moi...)
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

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

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    A vrai dire, et sans rien enlever à la qualité de ce qu'a présenté Ehonn, il y a quelques trucs dans ce qu'il a fait qui me chipote, et je suis occupé à réfléchir à un moyen sympa de corriger ce qu'il a fait...

    Mais comme je répond à plusieurs questions sur le forum pour l'instant, je laisse le temps à mon cerveau torturé pour qu'il me fasse sortir une solution raisonnable
    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. #11
    Membre chevronné Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 34
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    @Aspic
    As-tu considéré les lambda ?
    Voici l'appel avec la fonction bench qui prend une référence vers la lambda; et la fonction bench_copy qui prend une lambda (cela permet de créer la lambda dans l'appel de la fonction bench_copy).
    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
    // g++ -std=c++11 -Wall -Wextra -pedantic bench_fct.cpp -o bench_fct && bench_fct
     
    #include <iostream>
    #include <chrono>
    #include <thread>
     
     
    void sleep_ms(unsigned int const ms)
    {
    	std::this_thread::sleep_for(std::chrono::milliseconds(ms));
    }
     
     
    void fct1(int a)
    {
    	std::cout << "fct1, a = " << a << std::endl; sleep_ms(1000);
    }
     
    int fct2()
    {
    	 std::cout << "fct2" << std::endl; sleep_ms(2000);
    	 return 2;
    }
     
    void fct3(int & param)
    {
    	 std::cout << "fct3" << std::endl; sleep_ms(3000);
    	 param = 3;
    }
     
     
    template <class func>
    double bench(func & f)
    {
    	auto t_start = std::chrono::high_resolution_clock::now();
    	f(); // On exécute la fonction ou foncteur ou lambda
    	auto t_end = std::chrono::high_resolution_clock::now();
    	//return std::chrono::duration<double>(t_end - t_start).count(); // Retourne le temps en secondes
    	std::chrono::microseconds u = (t_end - t_start);
    	return u.count(); // Retourne le temps en microsecondes
    }
     
    template <class func>
    double bench_copy(func f)
    {
    	auto t_start = std::chrono::high_resolution_clock::now();
    	f(); // On exécute la fonction ou foncteur ou lambda
    	auto t_end = std::chrono::high_resolution_clock::now();
    	//return std::chrono::duration<double>(t_end - t_start).count(); // Retourne le temps en secondes
    	std::chrono::microseconds u = (t_end - t_start);
    	return u.count(); // Retourne le temps en microsecondes
    } 
     
     
    int main()
    {
    	// Reference
    	{
    		int i = 42;
    		std::cout << "Appel de fct1" << std::endl;
    		auto lambda = [&](){ fct1(i); };
    		double t = bench(lambda);
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	{
    		int i = 0;
    		std::cout << "Appel de fct2" << std::endl;
    		auto lambda = [&](){ i = fct2(); };
    		double t = bench(lambda);
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	{
    		int i = 0;
    		std::cout << "Appel de fct3" << std::endl;
    		auto lambda = [&](){ fct3(i); };
    		double t = bench(lambda);
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	// Copy
    	{
    		int i = 42;
    		std::cout << "Appel de fct1" << std::endl;
    		double t = bench_copy([&](){ fct1(i); });
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	{
    		int i = 0;
    		std::cout << "Appel de fct2" << std::endl;
    		double t = bench_copy([&](){ i = fct2(); });
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	{
    		int i = 0;
    		std::cout << "Appel de fct3" << std::endl;
    		double t = bench_copy([&](){ fct3(i); });
    		std::cout << "i = " << i << std::endl;
    		std::cout << "Temps = " << t << " secondes" << std::endl;
    		std::cout << std::endl;
    	}
     
    	return 0;
    }
    Et voila la sortie :
    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
    Appel de fct1
    fct1, a = 42
    i = 42
    Temps = 1.01813e+06 secondes
     
    Appel de fct2
    fct2
    i = 2
    Temps = 2.00152e+06 secondes
     
    Appel de fct3
    fct3
    i = 3
    Temps = 3.0017e+06 secondes
     
    Appel de fct1
    fct1, a = 42
    i = 42
    Temps = 1.00087e+06 secondes
     
    Appel de fct2
    fct2
    i = 2
    Temps = 2.00099e+06 secondes
     
    Appel de fct3
    fct3
    i = 3
    Temps = 3.00085e+06 secondes
    @koala01
    Ah ? Tu peux donner des indices sur ce qui ne va pas ?
    Je sais qu'il y a quelques trucs qui ne sont pas idéal:
    - le bench de la recherche dans la map et le risque d'erreur dans le nom du bench (solution: il faut récupérer le bench dans une référence puis utiliser cette référence pour le start et le stop)
    - le stop prend le temps entre lui et le dernier start
    - le typedef (peut être discutable)
    - la recherche du min et du max, le calcul de la médiane et de la moyenne à chaque fois qu'on récupère la valeur
    - le stockage de toutes les valeurs
    - la conversion souvent nécessaire pour avoir juste un std::map<std::string, double> qui ne contient que les minimums (par exemple)
    (mais bon ça a l'avantage de ne faire que 15 lignes et de répondre à mes besoins ^^)

  12. #12
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Citation Envoyé par Ehonn Voir le message
    @koala01
    Ah ? Tu peux donner des indices sur ce qui ne va pas ?
    Je sais qu'il y a quelques trucs qui ne sont pas idéal:
    - le bench de la recherche dans la map et le risque d'erreur dans le nom du bench (solution: il faut récupérer le bench dans une référence puis utiliser cette référence pour le start et le stop)
    - le stop prend le temps entre lui et le dernier start
    - le typedef (peut être discutable)
    - la recherche du min et du max, le calcul de la médiane et de la moyenne à chaque fois qu'on récupère la valeur
    - le stockage de toutes les valeurs
    - la conversion souvent nécessaire pour avoir juste un std::map<std::string, double> qui ne contient que les minimums (par exemple)
    (mais bon ça a l'avantage de ne faire que 15 lignes et de répondre à mes besoins ^^)
    Bah, des "détaux de conception", essentiellement, ne t'en fais pas

    je suis occupé à mettre mes idées au clair, et à voir ce qu'il est possible de faire (j'en profite pour m'habituer avec std::chrono ), mais je trouve qu'il manque un peu de SRP par ci par là, ou que le benchmark complet devrait pouvoir donner certains type de résultat qui n'apparaissent pas facilement avec ton code...

    Promis, d'ici la fin de la journée, vous aurez un roman comme je sais en faire sur le sujet
    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

  13. #13
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Avez vous vu une solution pour ce problème de bench de fonctions génériques ?
    Car quoiqu'il en soit bencher une fonction c'est toujours faire à un moment ou un autre :
    //demarrer chrono
    //appeler la fonction générique
    // arrêter chrono
    // calculer différence
    Du coup, je ne sais pas si ca passe avec les variadic templates.
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  14. #14
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Allez, avec un peu de retard (dont je m'en excuse, mais bon, j'ai aussi un vie privée, hein ), voici le résultat de mes cogitations.

    En introduction, il faut préciser (surtout à l'attention de Ehonn) que j'ai un gros défaut (en fait, non, je trouve que c'est plutot un qualité ) en terme de conception : je tiens particulièrement à respecter au mieux les principes énoncés par SOLID, et que c'est ce qui me laissait un "gout trop peu" dans ce qu'il a fait

    J'ai d'abord commencé par réfléchir à ce qui fait un bon benchmark, et les contraintes auxquelles ils est soumis.

    Cette réflexion m'a naturellement mené à réfléchir à ce qu'est un benchmark, et j'en suis arrivé à la conclusion que
    un benchmark est un test qui tend à mesurer les performances d'une fonctionnalité quelconque en terme de temps d'exécution
    C'est bien beau, mais il fallait donc préciser ce que signifient les termes mesuer une fonctionnalité en terme de temps d'exécution

    Car, a vrai dire, il y a deux possibilités pour ce faire :
    1. Soit en chronomètre simplement le temps qu'une fonctionnalité met pour s'exécuter
    2. Soit on compte le nombre de fois que l'on peut faire appel à cette fonctionnalité dans un délais imparti
    Autant le dire tout de suite, je ne me suis intéressé qu'à la première partie du problème, mais j'ai fait en sorte de nous laisser la possibilité d'évoluer de manière à gérer la deuxième partie

    Et il fallait bien sur définir ce que j'appelle une "fonctionnalité" :
    Une fonctionnalité est n'importe quelle fonction (ou fonction membre d'une classe que l'on est en mesure d'appeler
    Seulement, voilà...

    Pour que ce genre de test soit le plus représentatif possible, il faut qu'il limite au maximum l'impact que peuvent avoir certaines circonstances sur lesquelles l'utilisateur n'a aucune prise, comme (liste non exhaustive)
    • La charge du système au moment du test
    • le temps d'accès à des données qui ne sont pas disponibles en mémoire (accès réseau, accès disque dur, ...)
    • L'éventuelle mise en cache des données qui ne sont pas disponibles en mémoire
    • ...
    Il fallait donc se donner l'occasion de relancer plusieurs fois les différents tests à différents moment et garder le résultat de tous les tests de manière à pouvoir obtenir une durée moyenne pour chaque fonctionnalité.

    De plus, nous sommes souvent soumis à des contraintes au niveau du chronométrage lui-même (par exemple, certains systèmes ne permettent pas d'avoir une précision supérieure aux 0.5 millième de seconde).

    Si l'on essaye de tester une fonctionnalité qui s'exécute en moins de temps que ce qui n'est accessible en terme de précision du chronomètre, il faut que l'on soit malgré tout en mesure de disposer de valeurs "aussi fiables que possible".

    Il est donc nécessaire de permettre à un test d'effectuer plusieurs appels successifs de la fonction.

    Comme j'ai dit qu'il s'agissait de chronométrer la durée d'exécution des fonctionnalités, j'ai, tout naturellement, commencé par... créer une classe qui ne s'occupera que du chronométrage. Cette classe s'appelle Chronometer et prend la forme de
    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
     
    /** provides an easy way to get some duration between start() and stop()
      * functions
      *
      */
    class Chronometer
    {
    public:
        using clock = std::chrono::high_resolution_clock;
        using time_point = clock::time_point;
        using duration_type = clock::duration;
        void start()
        {
            beginTime_  = clock::now();
        }
        void stop()
        {
            duration_+= (clock::now() - beginTime_);
        }
        /** reset the current duration
          *
          * we may want to temporarily stop the chronometer, without losing the
          * current duration.
          * For that case, reset should be explicit
          */
        void reset()
        {
            duration_ = time_point();
        }
        /** allows the user to get the current duration.
          *
          */
        duration_type duration() const
        {
            return duration_.time_since_epoch();
        }
        duration_type partial()
        {
            stop();
            start();
            return duration();
        }
    private:
        time_point beginTime_;
        time_point duration_;
    };
    et peut s'utiliser assez facilement sous une forme proche de
    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
    int main()
    {
        chronometer chrono;
        chrono.start();
        /* des choses qui doivent etre chronometrees */
        auto partialTime = chrono.partial();
        /* encore des choses chronométrées */
        chrono.stop();
        auto duration = chrono.duration();
        chrono.start(); //reprise sans remise à zero du compteur 
        /* des choses chronométrées */
        chrono.stop();
        auto finalTime = chrono.duration();
        chrono.reset();
        /* chronométrage éventuel après remise à zero */
        return 0;   
    }
    Le tout en espérant fortement que la simplicité des fonctions et l'inlining permette de limiter au maximum l'overhead lié à l'appel de fonctions. Il faut garder en mémoire que l'inlining est décidé au final par le compilateur et qu'il n'est donc jamais acquis, mais, de manière générale, j'ai bon espoir que la simplicité des fonctions fera que le compilateur acceptera de les inliner

    Ensuite, ben, dés que l'on parle de tests, on parle inévitablement de résultats qu'il s'agit de pouvoir gérer.

    Nous devons donc être en mesure de gérer ces résultats sous la forme
    • d'un temps d'exécution pour une itération unique (comprenez : pour un appel unique de la fonction)
    • du temps d'exécution pour un ensemble d'itération (comprenez : le temps mis pour exécuter une boucle de X appels à la fonction)

    J'ai donc commencé par créer une structure simple qui me permette de gérer le temps d'exécution d'un appel de la fonction sous la forme de
    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
    #include <chrono>
     
    /** just a bunch of data which groups all data which concern one iteration
      *
      */
    struct IterationData
    {
        using clock = std::chrono::high_resolution_clock;
        using time_point = clock::time_point;
        using duration_type = clock::duration;
        IterationData(size_t run, size_t group, size_t it):
            runNumber(run),groupNumber(group), iterationNumber(it){}
        IterationData(size_t run, size_t group, size_t it,
                      duration_type const & dur):
            runNumber(run),groupNumber(group), iterationNumber(it),
            duration(dur){}
        /** Allows the user to determine which run the current iteration is
          * related to
          *
          */
        size_t runNumber;
        /** allows the user to know the iteration group  the current iteration is
          * related to
          *
          */
        size_t groupNumber;
        /** allows the user to know the number of the current iteration in the iteration loop
          *
          */
        size_t iterationNumber;
        duration_type duration;
        bool operator<(IterationData const & rhs) const
        {
            return duration < rhs.duration;
        }
    };
    Et, comme il est possible que l'exécution d'une fonction prenne un temps trop limité pour fournir une mesure utilisable, je me suis donné la possibilité de gérer un ensemble d'itérations sous la forme de
    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
    #include <chrono>
    #include <vector>
    #include <algorithm>
    #include <IterationData.hpp>
    /** manages the total loop execution duration
      *
      * in some cases, we have to call function in loop to be able to get some
      * representative duration.
      *
      * this class provides an easy way to get information about those function
      * loop call duration
      *
      */
    class IterationGroup
    {
        friend class BenchmarkBase;
    public:
        using clock = std::chrono::high_resolution_clock;
        using time_point = clock::time_point;
        using duration_type = clock::duration;
        IterationGroup(size_t run, size_t group):runNumber_(run),groupNumber_(group)
        {
     
        }
        duration_type const & minDuration() const
        {
            return std::min_element(iterations_.begin(),
                                    iterations_.end())->duration;
        }
        duration_type const & maxDuration() const
        {
            return std::max_element(iterations_.begin(),
                                    iterations_.end())->duration;
        }
        duration_type median() const
        {
            duration_type temp;
            for(auto toadd : iterations_)
                temp+=toadd.duration;
            return duration_type(temp.count()/iterations_.size());
        }
        duration_type deviation() const
        {
            duration_type temp;
            for(auto toadd : iterations_)
                temp+=squareDifferenceFromMedian(toadd.duration);
            std::chrono::duration<double,duration_type::period> result(sqrt(temp.count()));
            return std::chrono::duration_cast<duration_type>(result);
        }
        void add(duration_type const & toadd)
        {
            iterations_.push_back(IterationData(runNumber_,groupNumber_,
                                               iterations_.size()+1, toadd));
        }
        void setGroupDuration(duration_type const & dur)
        {
            groupDuration_ = dur;
        }
        size_t runNumber() const
        {
            return runNumber_;
        }
        size_t groupNumber() const
        {
            return groupNumber_;
        }
        duration_type groupDuration() const
        {
            return groupDuration_;
        }
     
    private:
        duration_type squareDifferenceFromMedian(duration_type const & p) const
        {
            auto med=median();
            auto difference = (p.count() < med.count() ? med.count() - p.count():
                                                         p.count() - med.count());
            return duration_type(difference * difference);
        }
        std::vector<IterationData> iterations_;
        size_t runNumber_;
        size_t groupNumber_;
        duration_type groupDuration_;
    };
    qui nous permet non seulement d'avoir la durée totale de l'exécution d'une boucle de N appels à la fonction, mais aussi d'obtenir des informations cohérentes relatives aux différents appels de la fonction.

    Evidemment, l'idée est de pouvoir faire cohabiter tout un ensemble de tests aussi divers que variés en terme de retrour de fonction comme en terme d'arguments transmis.

    Il me fallait donc une classe de base qui me fournisse une interface commune qui me permette de calculer le temps d'exécution de n'importe quel fonction.

    J'ai créé cette classe de base sous la forme de
    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
    #include <IterationGroup.hpp>
    #include <iostream>
    /** the benchmark base class
      *
      */
    class BenchmarkBase
    {
    public:
     
        BenchmarkBase(std::string const & name);
        BenchmarkBase(std::string const & name, size_t loopSize);
        BenchmarkBase(std::string const & name,size_t groupSize, size_t loopSize);
        virtual ~ BenchmarkBase();
        void execute()
        {
            std::cout<<"executing "<<name_<<"...";
            if(groupSize_ >1)
                executeMultiLoop();
            else if(loopSize_>1)
                executeSimpleLoop();
            else
                executeSimpleCall();
     
            std::cout<<"done"<<std::endl;
        }
        void nextRun()
        {
            ++runNumber_;
        }
        size_t runNumber() const
        {
            return runNumber_;
        }
     
        /** allows the user to fill a collection with all Iterations generated
          * by the execute function.
          *
          * @tparam Collection : any Iteration STL collection which doesn't use pair
          * @param[in,out] into the collection to be filled
          *
          */
        template< typename Collection>
        void allIterations(Collection & into) const
        {
            for(auto group : groups_)
            {
                for (auto iter: group.iterations_)
                    into.push_back(iter);
            }
        }
     
        /** allows the user to fill a collection with all IterationGroups generated
          * by the execute function.
          *
          * @tparam Collection : any IterationGroup STL collection which doesn't use pair
          * @param[in,out] into the collection to be filled
          *
          */
     
        template< typename Collection>
        void allIterationGroups(Collection & into) const
        {
            for(auto group : groups_)
            {
                into.push_back(group);
            }
        }
    protected:
        size_t loopSize() const{return loopSize_;}
        size_t groupSize() const {return groupSize_;}
        void addToGroups(IterationGroup const & group)
        {
            groups_.push_back(group);
        }
        size_t groupCount()const {return groups_.size();}
    private:
        virtual void executeMultiLoop();
        virtual void executeSimpleLoop() = 0;
        virtual void executeSimpleCall() = 0;
        std::string name_;
        size_t runNumber_;
        size_t groupSize_;
        size_t loopSize_;
        std::vector<IterationGroup> groups_;
    };
    La responsabilité de cette classe est uniquement de gérer l'exécution d'un certain nombre de boucles dans lesquelles une fonction sera appelée un certain nombre de fois, et bien sur, de maintenir les résultats du chronométrage de ces appels de fonction.

    L'implémentation des fonctions prend la forme de
    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
     
    #include "BenchmarkBase.hpp"
     
        BenchmarkBase::BenchmarkBase(std::string const & name):name_(name),
                      runNumber_(1),
                      groupSize_(10),
                      loopSize_(50){}
        BenchmarkBase::BenchmarkBase(std::string const & name, size_t loopSize):
                      name_(name),
                      runNumber_(1),
                      groupSize_(10),
                      loopSize_(loopSize){}
        BenchmarkBase::BenchmarkBase(std::string const & name, size_t groupSize,
                                     size_t loopSize):
                      name_(name),
                      runNumber_(1),
                      groupSize_(groupSize),
                      loopSize_(loopSize){}
     
    BenchmarkBase::~BenchmarkBase()
    {
        //dtor
    }
     
    void BenchmarkBase::executeMultiLoop()
    {
        for(size_t i = 0; i< groupSize(); ++i)
        {
            if(this->loopSize()>1)
            {
                executeSimpleLoop();
            }
     
            else
            {
                executeSimpleCall();
            }
        }
    }
    Bien sur, il fallait être en mesure d'appeler aussi bien des fonctions libres que des fonctions membres de classes ou de structures, et c'est susceptible de varier aussi bien du point de vue de type de retour que du point de vue du nombre et du type des paramètres à transmettre.

    std::function, fournie par C++11 est une aide très efficace dans le sens où cette classe permet de faciliter grandement la création de foncteurs.

    Seulement, elle nécessite de transmettre les arguments au moment de l'appel de l'opérateur ().

    Son utilisation "classique" est en effet du genre de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    void foo(int i, int j, int k)
    {
        /* utilise i, j et k */
    }
    int main()
    {
        std::function<void(int, int, int)> functor;
        functor(2, 3, 4);
        return 0;
    }
    Cependant, cela ne correspond pas à la manière dont je veux utiliser mes foncteurs car, si je dois transmettre les arguments lors de l'appel de la fonction, je perds la possibilité d'utiliser le polymorphisme en appelant simplement la fonction execute():
    j'aurais en effet un prototype différents d'appel pour chaque nombre et chaque type de paramètre transmis, ce qui deviendrait rapidement ingérable.

    Il m'a donc fallut créer une structure qui me permette de transmettre les paramètres en "attente d'une utilisation ultérieure", et ce, aussi bien pour les fonctions libres et pour les fonctions membre de classes / structures.

    Au final, j'ai créer une structure pour l'appel des fonctions libres et une autre pour l'appel des fonctions membres sous les formes de
    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
    template<typename Ret, typename ...Args>
    struct FreeFunctionCaller
    {
        std::function< Ret() > fun_;
        FreeFunctionCaller( Ret (*fun)(Args...) , Args... args):
            fun_(std::bind(fun,args...)){}
        template<typename T = Ret>
        typename std::enable_if<std::is_same<T, void>::value>::type
         exec()
        {
            fun_();
     
        }
     
        Ret exec()
        {
            return fun_();
     
        }
    };
    et de
    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
     
    template<typename Ret,typename Type ,typename ...Args>
    struct MemberFunctionCaller
    {
        std::function< Ret() > fun_;
        MemberFunctionCaller( Ret (Type::*fun)(Args...) , Type object, Args... args):
            fun_(std::bind(fun,object, args...)){}
        template<typename T = Ret>
        typename std::enable_if<std::is_same<T, void>::value>::type
         exec()
        {
            fun_();
     
        }
     
        Ret exec()
        {
            return fun_();
     
        }
    };
    qui permettent une utilisation proche de

    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
    int multiply(int i, int j)
    {
        std::cout<<"reslutat : "<<i*j<<std::endl;
        return i*j;
    }
    struct MyStruct
    {
        void foo() const{std::cout<<"foo"<<std::endl;}
        void bar(int i) const{std::cout<<"bar with i="<<i<<std::endl;}
    };
    int main()
    {
        FreeFunctionCaller<int, int, int> testFreFunction(multiply,5,9);
        testFreFunction.exec();
     
        MyStruct s;
        MemberFunctionCaller<void, MyStruct const> testMemberFoo(&MyStruct::foo,s);
        MemberFunctionCaller<void, MyStruct const,int> testMemberBar(&MyStruct::bar,s,1);
     
        testMemberFoo.exec();
        testMemberBar.exec();
        return 0;
    }
    Et j'ai pu utiliser pour définir des classes concrètes de benchmark sous la forme de
    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
     
    template<typename Ret, typename ...Args>
    class FreeFunctionBench :public BenchmarkBase,
                       private FreeFunctionCaller<Ret, Args ...>
    {
        public:
        FreeFunctionBench(std::string const & name,
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase(name),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        FreeFunctionBench(std::string const & name, size_t loopSize,
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase(name, loopSize),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        FreeFunctionBench(std::string const & name,
                            size_t groupSize,
                            size_t loopSize,
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase(name, groupSize, loopSize),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        private:
                struct SinglePass
                {
                    SinglePass( std::function< Ret() > &fun ):fun(fun){}
                    std::function< Ret() > &fun;
                    Chronometer chrono;
                    void call()
                    {
                        chrono.start();
                        fun();
                        chrono.stop();
                    }
                };
            void executeSimpleLoop()
            {
                IterationGroup tempGroup(runNumber(),groupCount()+1);
                std::vector<SinglePass> tab;
                size_t toadd =BenchmarkBase::loopSize();
                for(size_t i = 0; i< toadd;++i)
                {
                    tab.push_back(SinglePass(FreeFunctionCaller<Ret, Args ...>::fun_));
                }
                Chronometer chrono;
                chrono.start();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tab[i].call();
                }
                chrono.stop();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tempGroup.add(tab[i].chrono.duration());
                }
                tempGroup.setGroupDuration(chrono.duration());
                addToGroups(tempGroup);
            }
            void executeSimpleCall()
            {
                IterationGroup group(runNumber(),groupCount()+1);
                Chronometer chrono;
                chrono.start();
                this->exec();
                chrono.stop();
                group.add(chrono.duration());
                group.setGroupDuration(chrono.duration());
                addToGroups(group);
            }
    };
    et de
    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
     
    template<typename Ret,typename Type ,typename ...Args>
    class MemberFunctionBench :public BenchmarkBase,
                       private MemberFunctionCaller<Ret,Type, Args ...>
    {
        public:
        MemberFunctionBench(std::string const & name,
                            Ret (Type::*fun)(Args...) , Type object, Args... args):
            BenchmarkBase(name),
            MemberFunctionCaller<Ret, Type, Args ...>(fun, object, args...){}
        MemberFunctionBench(std::string const & name,
                            size_t loopSize,
                            Ret (Type::*fun)(Args...) , Type object, Args... args):
            BenchmarkBase(name,loopSize),
            MemberFunctionCaller<Ret, Type, Args ...>(fun, object, args...){}
        MemberFunctionBench(std::string const & name,
                            size_t groupSize,
                            size_t loopSize,
                            Ret (Type::*fun)(Args...) , Type object, Args... args):
            BenchmarkBase(name,groupSize, loopSize),
            MemberFunctionCaller<Ret, Type, Args ...>(fun, object, args...){}
        private:
                struct SinglePass
                {
                    SinglePass( std::function< Ret() > &fun ):fun(fun){}
                    std::function< Ret() > &fun;
                    Chronometer chrono;
                    void call()
                    {
                        chrono.start();
                        fun();
                        chrono.stop();
                    }
                };
            void executeSimpleLoop()
            {
                IterationGroup tempGroup(runNumber(),groupCount()+1);
                std::vector<SinglePass> tab;
                size_t toadd =BenchmarkBase::loopSize();
                for(size_t i = 0; i< toadd;++i)
                {
                    tab.push_back(SinglePass(MemberFunctionCaller<Ret,Type, Args ...>::fun_));
                }
                Chronometer chrono;
                chrono.start();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tab[i].call();
                }
                chrono.stop();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tempGroup.add(tab[i].chrono.duration());
                }
                tempGroup.setGroupDuration(chrono.duration());
                addToGroups(tempGroup);
            }
            void executeSimpleCall()
            {
                IterationGroup group(runNumber(),groupCount()+1);
                Chronometer chrono;
                chrono.start();
                this->exec();
                chrono.stop();
                group.add(chrono.duration());
                group.setGroupDuration(chrono.duration());
                addToGroups(group);
            }
    };
    Et que l'on peut appeler sous la forme de
    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
    int multiply(int i, int j)
    {
        std::cout<<"reslutat : "<<i*j<<std::endl;
        return i*j;
    }
    struct MyStruct
    {
        void foo() const{std::cout<<"foo"<<std::endl;}
        void bar(int i) const{std::cout<<"bar with i="<<i<<std::endl;}
    };
    int main()
    {
        FreeFunctionBench<int, int, int> testFreeFunction("multiply",multiply,5,9);
        testFreeFunction.execute();
     
        MyStruct s;
        MemberFunctionBench<void, MyStruct const> testMemberFoo("MyStruct::foo",&MyStruct::foo,s);
        MemberFunctionBench<void, MyStruct const,int> testMemberBar("MyStruct::bar",&MyStruct::bar,s,1);
     
        testMemberFoo.execute();
        testMemberBar.execute();
        return 0;
    }
    Maintenant, nous pouvons parfaitement envisager d'écrire un code proche de
    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
    int multiply(int i, int j)
    {
        std::cout<<"reslutat : "<<i*j<<std::endl;
        return i*j;
    }
    struct MyStruct
    {
        void foo() const{std::cout<<"foo"<<std::endl;}
        void bar(int i) const{std::cout<<"bar with i="<<i<<std::endl;}
    };
    int main()
    {
        MyStruct s;
        std::vector<BenchmarkBase *> tab;
        tab.push_back(new FreeFunctionBench<int, int, int>
                          ("multiply",multiply,5,9));
        tab.push_back(new MemberFunctionBench<void, MyStruct const>
                          ("MyStruct::foo",&MyStruct::foo,s));
        tab.push_back(new MemberFunctionBench<void, MyStruct const,int>
                          ("MyStruct::bar",&MyStruct::bar,s,1));
        for (auto it : tab)
            it->execute();
        for(auto it : tab)
        {
            std::vector<IterationGroup> itGroup;
            it->allIterationGroups(itGroup);
            std::cout<<" calls of "<<it->name()<<std::endl;
            std::cout<<"#group\tMinTime\tMaxTime\tTotalTime\tMedianTime"<<std::endl;
            for(auto data : itGroup)
            {
                std::cout<<data.groupNumber()
                         <<"\t"<<data.minDuration().count()
                         <<"\t"<<data.maxDuration().count()
                         <<"\t"<<data.groupDuration().count()
                         <<"\t\t"<<data.median().count()
                         <<std::endl;
            }
        }
        return 0;
    }
    Evidemment, c'est très certainement encore perfectible dans le sens où l'on pourrait envisager la création
    • d'une classe qui regrouperait, par exemple, l'ensemble des IterationGroup d'un test particulier et qui sortirait une moyenne globale, basée sur l'ensemble des IterationGroup
    • d'une "Fabrique" de benchmarks
    • et plein de choses encore...
    mais je crois que si l'on part sur cette base, il y a vraiment des trucs sympa à faire
    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

  15. #15
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Superbe réponse comme d'hab

    Le temps que je potasse ta réponse, j'ai juste une petite question sur le bench :

    J'aimerais automatiquement afficher le nom de la fonction benchée sur la console par exemple :
    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
     
    void foo(int a)
    {
     
    }
     
    template <class func>
            void Bench(func& f)
            {
                f(); // appel de la fonction à bencher
     
                std::cout << "Nom de la fonction benchee : " << ??? << std::endl;
            }
     
    int main()
    {
    // cette  ligne ne compile pas, c'est juste pour l'exemple
    Bench(foo(5));
    }
    J'aimerais afficher : "Nom de la fonction benchee : foo"

    Alors j'ai vu qu'on pouvait utiliser une macro __FUNC__ mais si je la met dans la fonction Bench alors je n'aurais pas la bonne sortie.

    Y'a t-il un moyen de faire cela ?

    Merci
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  16. #16
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Si tu regardes le code final que j'ai donné, le nom de la fonction est déjà indiqué
    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
     
    int multiply(int i, int j)
    {
        std::cout<<"reslutat : "<<i*j<<std::endl;
        return i*j;
    }
    struct MyStruct
    {
        void foo() const{std::cout<<"foo"<<std::endl;}
        void bar(int i) const{std::cout<<"bar with i="<<i<<std::endl;}
    };
    int main()
    {
        MyStruct s;
        std::vector<BenchmarkBase *> tab;
        tab.push_back(new FreeFunctionBench<int, int, int>
                          ("multiply",multiply,5,9));
        tab.push_back(new MemberFunctionBench<void, MyStruct const>
                          ("MyStruct::foo",&MyStruct::foo,s));
        tab.push_back(new MemberFunctionBench<void, MyStruct const,int>
                          ("MyStruct::bar",&MyStruct::bar,s,1));
        for (auto it : tab)
            it->execute();
        for(auto it : tab)
        {
            std::vector<IterationGroup> itGroup;
            it->allIterationGroups(itGroup);
     
            std::cout<<" calls of "<<it->name()<<std::endl; // <---C'est ici :)
     
            std::cout<<"#group\tMinTime\tMaxTime\tTotalTime\tMedianTime"<<std::endl;
            for(auto data : itGroup)
            {
                std::cout<<data.groupNumber()
                         <<"\t"<<data.minDuration().count()
                         <<"\t"<<data.maxDuration().count()
                         <<"\t"<<data.groupDuration().count()
                         <<"\t\t"<<data.median().count()
                         <<std::endl;
            }
        }
        return 0;
    }
    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

  17. #17
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Oui j'avais vu

    Mais en fait tu nommes la fonction en dur :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    FreeFunctionBench<int, int, int> testFreeFunction("multiply",multiply,5,9);
    Et je me demandais si on pouvait le faire automatiquement (en d'autres termes, supprimer le premier paramètre de FreeFunctionBench) ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  18. #18
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 614
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 614
    Points : 30 626
    Points
    30 626
    Par défaut
    Ah, au temps pour moi... je n'avais pas bien compris ta question

    Mais il faut savoir que le nom d'une fonction n'existe plus au runtime, et qu'il faut donc essayer de le récupérer au niveau de la compilation

    Il faut aussi savoir que la macro __FUNCTION__ n'est, apparemment, pas standard, et que tu ne peux donc pas l'utiliser "telle quelle".

    Pour des fonctions qui ne varieraient ni en terme de valeur de retour, ni en terme de nombre ou de type d'argument, tu pourrait te baser sur boost::preprocesseur, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    #include <iostream>
    #include <map>
    #include <string>
    #include <boost/preprocessor/stringize.hpp>
     
    typedef void (*fptr_t)(char*);
    typedef std::map<fptr_t, std::string> function_map_t;
    function_map_t fmap;
     
    #define REGISTER_FUNCTION(f) fmap[f] = BOOST_PP_STRINGIZE(f);
    et modifier le code des constructeurs sous une forme proche de
    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
     
    template<typename Ret, typename ...Args>
    class FreeFunctionBench :public BenchmarkBase,
                       private FreeFunctionCaller<Ret, Args ...>
    {
        public:
        FreeFunctionBench(
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase( fmap[fun]),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        FreeFunctionBench(size_t loopSize,
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase(fmap[fun], loopSize),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        FreeFunctionBench(size_t groupSize,
                            size_t loopSize,
                          Ret (*fun)(Args...) , Args... args):
            BenchmarkBase(fmap[fun], groupSize, loopSize),
            FreeFunctionCaller<Ret, Args ...>(fun, args...){}
        private:
                struct SinglePass
                {
                    SinglePass( std::function< Ret() > &fun ):fun(fun){}
                    std::function< Ret() > &fun;
                    Chronometer chrono;
                    void call()
                    {
                        chrono.start();
                        fun();
                        chrono.stop();
                    }
                };
            void executeSimpleLoop()
            {
                IterationGroup tempGroup(runNumber(),groupCount()+1);
                std::vector<SinglePass> tab;
                size_t toadd =BenchmarkBase::loopSize();
                for(size_t i = 0; i< toadd;++i)
                {
                    tab.push_back(SinglePass(FreeFunctionCaller<Ret, Args ...>::fun_));
                }
                Chronometer chrono;
                chrono.start();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tab[i].call();
                }
                chrono.stop();
                for(size_t i = 0; i< tab.size();++i)
                {
                    tempGroup.add(tab[i].chrono.duration());
                }
                tempGroup.setGroupDuration(chrono.duration());
                addToGroups(tempGroup);
            }
            void executeSimpleCall()
            {
                IterationGroup group(runNumber(),groupCount()+1);
                Chronometer chrono;
                chrono.start();
                this->exec();
                chrono.stop();
                group.add(chrono.duration());
                group.setGroupDuration(chrono.duration());
                addToGroups(group);
            }
    };
    et ta fonction main pourrait donc ressembler à quelque chose comme

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    void sha1(char*) {}  // a function you want to lookup
     
    int main() {
          REGISTER_FUNCTION(sha1)
          FreeFunctionBench<void,char*>(sha1,"salut");
          return 0;
    }
    mais cela ne résoudrait qu'une partie du problème, car:
    • tu serait obligé d'enregistrer chaque fonction expliciement
    • il te faudrait un typedef pour chaque type de pointeur de fonction que tu pourra rencontrer (c'est à dire pour chaque variation du type de retour ou d'argument)...
    je ne suis vraiment pas persuadé que la solution ne soit pas au final encore pire que le problème
    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

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

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    Points : 2 160
    Points
    2 160
    Par défaut
    Merci pour cette (longue et détaillée) réponse koala01

  20. #20
    Expert confirmé
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Points : 4 388
    Points
    4 388
    Par défaut
    Citation Envoyé par koala01 Voir le message
    je ne suis vraiment pas persuadé que la solution ne soit pas au final encore pire que le problème
    Je pense que tu as raison, on va laisser comme cela alors

    Merci beaucoup
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. [VBA-E]Executer fonction VBA sur Excel sans activer la macro
    Par marie10 dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 19/01/2006, 14h34
  2. Fonctions génériques et listes
    Par DevloNewb' dans le forum C++
    Réponses: 6
    Dernier message: 13/01/2006, 14h47
  3. executer fonction automatiquement
    Par zamanika dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 05/11/2004, 13h42
  4. Valider avant execution fonction
    Par jeff37 dans le forum ASP
    Réponses: 3
    Dernier message: 08/09/2004, 12h39
  5. [C#] MDI Execution fonction sur fille a partir de la mere
    Par alex57 dans le forum Windows Forms
    Réponses: 2
    Dernier message: 27/07/2004, 10h00

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