Partie 2 : Mesurer le temps passé par une fonction
Références
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
int factorial(int n)
{
return n == 0 ? 1 : (n * factorial(n - 1));
}
La fonction aura le squelette suivant:
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é.
}
Ceci est assez simple à réaliser en utilisant les fonctions std::chrono::high_resolution_clock::now()
et std::make_pair()
.
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);
}
Question n°2
Transformer la fonction précédente
estimate_time
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.
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 :
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);
}
Question n°3
Nous souhaitons désormais pouvoir prendre une fonction pouvant prendre plusieurs arguments comme la fonction puissance.
template<class numericalT>
numericalT power_by_int(numericalT x, int y)
{
numericalT result = (numericalT)1.0;
while (y-- > 0)
result *= x;
return result;
}
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.
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);
}
Estimer le temps nécesaire pour calculer par exemple : 1.0002 ^ 10000000.
Le code suivant va permettre d'estimer le temps passé à calculer le résultat :
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";
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 :
template<class Function, class ... Args>
long long mean_function_time(int number_of_runs, Function function, Args... arguments)
{
...
}
Il suffit de lancer number_of_runs
fois un appel à estimate_function_time
pour la fonction et les arguments. Ceci donne le code suivant :
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;
}
Manipuler un nombre variable de paramètres
Exécution à la compilation