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++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert
    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
    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 Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    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
    Membre Expert
    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
    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 Expert Avatar de Ehonn
    Homme Profil pro
    Étudiant
    Inscrit en
    Février 2012
    Messages
    788
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France

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

    Informations forums :
    Inscription : Février 2012
    Messages : 788
    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
    Membre Expert
    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
    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
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    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

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

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