This shows you the differences between two versions of the page.
— |
in204:tds:sujets:td12_2023:part2 [2023/12/04 11:23] (current) bmonsuez created |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Partie 2 : Mesurer le temps passé par une fonction ====== | ||
+ | |||
+ | [[in204:tds:sujets:td12_2023|TD12]] | ||
+ | |||
+ | ===== Références===== | ||
+ | |||
+ | [[https://en.cppreference.com/w/cpp/chrono/high_resolution_clock|std::chrono::high_resolution_clock]] | ||
+ | |||
+ | [[https://en.cppreference.com/w/cpp/utility/pair/make_pair|str::make_pair(...)]] | ||
+ | |||
+ | [[https://en.cppreference.com/w/cpp/utility/pair/pair|str::pair<T1, T2>]] | ||
+ | |||
+ | ====== Partie n°1 ====== | ||
+ | |||
+ | Nous souhaitons mesurer le temps de calcul d'une fonction. Pour ce faire, nous souhaitons créer une fonction : | ||
+ | - qui va prendre en argument la fonction que nous souhaitons exécuter, | ||
+ | - les arguments que nous devons passer à cette fonction, | ||
+ | - qui va lancer un chronomètre, | ||
+ | - qui va lancer la fonction | ||
+ | - qui va récupérer le résultat de la fonction, | ||
+ | - qui va estimer le temps passé par le temps de la fonction, | ||
+ | - qui va retourner à la fois le résultat de la fonction mais aussi le temps passé par la fonction. | ||
+ | |||
+ | ===== Question n°1 ===== | ||
+ | |||
+ | Pour simplifier la conception, écrivez dans un premier la fonction qui appelle la fonction factorielle qui suit: | ||
+ | et qui va exécuter cette fonction | ||
+ | |||
+ | <code cpp> | ||
+ | int factorial(int n) | ||
+ | { | ||
+ | return n == 0 ? 1 : (n * factorial(n - 1)); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | La fonction aura le squelette suivant: | ||
+ | <code cpp> | ||
+ | std::pair<std::chrono::high_resolution_clock::duration, int> estimate_factorial_time(int n) | ||
+ | { | ||
+ | // Code pour lancer le chronomètre | ||
+ | int result = factorial(n); | ||
+ | // Code pour calculer le temps écoulé | ||
+ | // Retourner la paire contenant le résultat et le temps passé. | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Ceci est assez simple à réaliser en utilisant les fonctions ''std::chrono::high_resolution_clock::now()'' et ''std::make_pair()''. | ||
+ | |||
+ | <code cpp> | ||
+ | std::pair<std::chrono::high_resolution_clock::duration, long double> estimate_factorial_time(int n) | ||
+ | { | ||
+ | auto starting_time = std::chrono::high_resolution_clock::now(); | ||
+ | auto result = factorial(n); | ||
+ | auto elasped_time = std::chrono::high_resolution_clock::now() - starting_time; | ||
+ | return std::make_pair(elasped_time, result); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ===== Question n°2 ===== | ||
+ | |||
+ | Transformer la fonction précédente <code>estimate_time</code> pour qu'elle prenne en argument une fonction arbitraire qui prend un argument et retourne un résultat qui ne sera pas obligatoirement un résultat de type ''long double''. | ||
+ | |||
+ | Il faudra penser à utiliser un modèle (template) de fonctions. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Il suffit de modifier la fonction antérieure comme un fonction qui prend deux paramètres de types: | ||
+ | - le type correspondant à la fonction, | ||
+ | - le type correspondant au paramètre de la fonction. | ||
+ | |||
+ | Et le tour est joué. Ceci donne le code suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | template<class Function, class T> | ||
+ | auto estimate_function_time(Function function, T argument) | ||
+ | { | ||
+ | auto starting_time = std::chrono::high_resolution_clock::now(); | ||
+ | auto result = function(argument); | ||
+ | auto elasped_time = std::chrono::high_resolution_clock::now() - starting_time; | ||
+ | return std::make_pair(elasped_time, result); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ===== Question n°3 ===== | ||
+ | |||
+ | Nous souhaitons désormais pouvoir prendre une fonction pouvant prendre plusieurs arguments comme la fonction puissance. | ||
+ | |||
+ | <code cpp> | ||
+ | template<class numericalT> | ||
+ | numericalT power_by_int(numericalT x, int y) | ||
+ | { | ||
+ | numericalT result = (numericalT)1.0; | ||
+ | while (y-- > 0) | ||
+ | result *= x; | ||
+ | return result; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Modifier le code de la fonction pour pouvoir prendre une telle fonction comme paramètre. | ||
+ | |||
+ | __**Remarque**__: il faut penser à utiliser les **packs de paramètres** que nous avons vu dans la première partie du TD. | ||
+ | |||
+ | <hidden Correction> | ||
+ | Dans le cas présent, comme nous avons aucun, un ou plusieurs paramètres, chacun des paramètres pouvant avoir un type différent. Dans ce cas, nous pouvons remplacer le paramêtre ''T'' de la fonction ''estimate_function_time'' précédenter par un argument correspondant à un pack de paramètres ''...Args''. Et c'est tout ! | ||
+ | |||
+ | template<class Function, class ... Args> | ||
+ | auto estimate_function_time(Function function, Args... arguments) | ||
+ | { | ||
+ | auto starting_time = std::chrono::high_resolution_clock::now(); | ||
+ | auto result = function(arguments...); | ||
+ | auto elasped_time = std::chrono::high_resolution_clock::now() - starting_time; | ||
+ | return std::make_pair(elasped_time, result); | ||
+ | } | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | Estimer le temps nécesaire pour calculer par exemple : 1.0002 ^ 10000000. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Le code suivant va permettre d'estimer le temps passé à calculer le résultat : | ||
+ | |||
+ | <code cpp> | ||
+ | auto pw = estimate_function_time(power_by_int<long double>, 1.0002, 1000000); | ||
+ | std::cout << "Computing 1.02^1000000=" << pw.second << " in " << pw_10000000.first.count() << " ticks.\n"; | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ==== Question n°5 ==== | ||
+ | |||
+ | Créer une fonction nouvelle qui va appeller la fonction ''estimate_function_time'' pour calculer ''x'' fois le temps nécessaire pour calculer la fonction et qui retourn le temps moyen pour effectuer un "run" de la fonction. | ||
+ | |||
+ | |||
+ | Cette fonction aura l'entête suivante : | ||
+ | |||
+ | <code cpp> | ||
+ | template<class Function, class ... Args> | ||
+ | long long mean_function_time(int number_of_runs, Function function, Args... arguments) | ||
+ | { | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Il suffit de lancer ''number_of_runs'' fois un appel à ''estimate_function_time'' pour la fonction et les arguments. Ceci donne le code suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | template<class Function, class ... Args> | ||
+ | long long mean_function_time(int number_of_runs, Function function, Args... arguments) | ||
+ | { | ||
+ | long long duration = 0L; | ||
+ | int remaining_runs = number_of_runs; | ||
+ | while (remaining_runs-- > 0) | ||
+ | duration += estimate_function_time(function, arguments...).first.count(); | ||
+ | return duration / (long long)number_of_runs; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | ====== Navigation ====== | ||
+ | |||
+ | Partie 1: [[.part1|Manipuler un nombre variable de paramètres]]\\ | ||
+ | Partie 3: [[.part3|Exécution à la compilation]] | ||