This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:tds:sujets:td9:part3 [2021/11/08 12:24] bmonsuez [Question 2 :] |
in204:tds:sujets:td9:part3 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== Partie 3 : Exécution à la compilation ====== | + | ====== Exécution asynchrone ====== |
[[in204:tds:sujets:td9|TD9]] | [[in204:tds:sujets:td9|TD9]] | ||
Line 5: | Line 5: | ||
===== Références===== | ===== Références===== | ||
+ | [[http://en.cppreference.com/w/cpp/thread/async|std::async]] [[http://en.cppreference.com/w/cpp/thread/future|std::future]] | ||
- | ===== Question 1 : ===== | + | ===== Question n°1 ===== |
- | + | ||
- | Nous nous intéressons à l'estimation du temps de calcul des fonction ''factorial'' et des fonctions ''power_by_int'' que nous avons défini précédemment. | + | |
+ | Nous considérons la fonction suivante qui calcule les décimales de « e ». | ||
<code cpp> | <code cpp> | ||
- | auto fn = estimate_function_time(factorial, 100); | + | std::string computeE(int numberOfDigits) |
- | std::cout << "Computing fact(100)=" << fn .second << " in " << fn.first.count() << " ticks.\n"; | + | |
- | auto pw = estimate_function_time(power_by_int<long double>, 1.0002, 1000000); | + | |
- | std::cout << "Computing 1.02^1000000=" << pw.second << " in " << pw.first.count() << " ticks.\n"; | + | |
- | </code> | + | |
- | + | ||
- | Ceci nous retourne le temps mis pour calculer la fonction ''factorial'' et pour la fonction ''power_by_int''. | + | |
- | + | ||
- | Expérimenter. | + | |
- | + | ||
- | ===== Question 2 : ===== | + | |
- | + | ||
- | + | ||
- | Nous souvaitons indiquer au compilateur qu'il peut calculer au moment de la compilation les expressions si celles-ci sont constantes. | + | |
- | + | ||
- | Pour ce faire nous ajoutons le mot-clé ''constexpr'' devant la fonction ou l'expression dont la valeur peut-être exécuté au moment de la compilation. | + | |
- | + | ||
- | Ainsi, nous pouvons indiquer que les deux fonctions ''factorial'' et ''power_by_int'' peuvent être calculer au moment de la compilation si les arguments sont des valeurs définies au moment de la compilation. | + | |
- | + | ||
- | <code cpp> | + | |
- | constexpr long double factorial(int n) | + | |
{ | { | ||
- | return n == 0 ? 1 : n * factorial(n - 1); | + | int sizeOfTable = numberOfDigits + 9; |
- | } | + | int* table = (int*)_alloca(sizeOfTable * sizeof(numberOfDigits)); |
+ | table[0] = 0; | ||
+ | table[1] = 2; | ||
+ | for (int i = sizeOfTable - 1; i > 0; i--) { | ||
+ | table[i] = 1; | ||
+ | } | ||
- | template<class numericalT> | + | std::ostringstream output; |
- | constexpr numericalT power_by_int(numericalT x, int y) | + | int x = 0; |
- | { | + | table[1] = 2; |
- | numericalT result = (numericalT)1.0; | + | for (; sizeOfTable > 9; sizeOfTable -- ) |
- | while (y-- > 0) | + | { |
- | result *= x; | + | for (int i = sizeOfTable - 1; i > 0; i--) |
- | return result; | + | { |
+ | table[i] = x % i; | ||
+ | x = 10 * table[i - 1] + x / i; | ||
+ | } | ||
+ | output << x; | ||
+ | } | ||
+ | return output.str(); | ||
} | } | ||
</code> | </code> | ||
- | Ceci autorise le compilateur a compilé l'expression au moment de la compilation. | + | Implanter la fonction et vérifier que celle-ci fonctionne correctement. |
- | + | <hidden Correction> | |
- | ===== Question 1 : ===== | + | |
- | + | ||
- | Tester le code suivant: | + | |
<code cpp> | <code cpp> | ||
+ | #include<sstream> | ||
+ | #include<iostream> | ||
- | auto fn = estimate_function_time(factorial, 100); | + | std::string computeE(int numberOfDigits) |
- | std::cout << "Computing fact(100)=" << fn.second << " in " << fn.first.count() << " ticks.\n"; | + | { |
+ | int sizeOfTable = numberOfDigits + 9; | ||
+ | int* table = (int*)_alloca(sizeOfTable * sizeof(numberOfDigits)); | ||
+ | table[0] = 0; | ||
+ | table[1] = 2; | ||
+ | for (int i = sizeOfTable - 1; i > 0; i--) { | ||
+ | table[i] = 1; | ||
+ | } | ||
- | auto pw = estimate_function_time(power_by_int<long double>, 1.0002, 1000000); | + | std::ostringstream output; |
- | std::cout << "Computing 1.02^100000=" << pw.second << " in " << pw.first.count() << " ticks.\n"; | + | int x = 0; |
+ | table[1] = 2; | ||
+ | for (; sizeOfTable > 9; sizeOfTable--) | ||
+ | { | ||
+ | for (int i = sizeOfTable - 1; i > 0; i--) | ||
+ | { | ||
+ | table[i] = x % i; | ||
+ | x = 10 * table[i - 1] + x / i; | ||
+ | } | ||
+ | output << x; | ||
+ | } | ||
+ | return output.str(); | ||
+ | } | ||
+ | int main() | ||
+ | { | ||
+ | std::string value = computeE(100); | ||
+ | std::cout << "e with " << 100 << " decimals\n" << value << std::endl; | ||
+ | } | ||
</code> | </code> | ||
+ | </hidden> | ||
+ | ===== Question n°2 ===== | ||
+ | Nous constatons que calculer 10000 ou 20000 décimales de e, cela prend du temps. Nous souhaitons transformer cela en une fonction asynchrone à l’aide de la fonction [[http://en.cppreference.com/w/cpp/thread/async|std::async]]. | ||
- | A votre avis ? Est-ce que le compilateur à générer le code au moment de la compilation ? | + | Ecrire le code transformant la précédente fonction en une fonction asynchrone en utilisant la fonction [[http://en.cppreference.com/w/cpp/thread/async|std::async]]. |
+ | Au lieu d'appeller directement la fonction ''computeE'', nous appellons la fonction ''computeE'' au travers d'un appel à la fonction ''std::async'' qui va exécuter la fonction ''computeE'' de manière asynchone et retourner un objet de type ''std::future<string>'' qui va permettre de savoir si le résultat et disponible et aussi d'avoir la valeur du résultat quand celui-ci sera disponible. | ||
- | ===== Question 2 : ===== | ||
- | |||
- | En fait, nous pouvons aider naivement le compilateur en faisant bien apparaître le paramètre constant : | ||
- | |||
- | <code cpp> | ||
- | factorial_100 = []() { return factorial(100); }; | ||
- | fn = estimate_function_time(factorial_100); | ||
- | std::cout << "Computing fact(100)=" << fn.second << " in " << fn.first.count() << " ticks.\n"; | ||
- | | ||
- | power_10002_100000 = []() { return power_by_int<long double>, 1.0002, 100000); }; | ||
- | pw = estimate_function_time(power_10002_100000); | ||
- | std::cout << "Computing 1.02^1000000=" << pw.second << " in " << pw.first.count() << " ticks.\n"; | ||
- | </code> | ||
- | |||
- | |||
- | Est-ce que cela améliore les résultats ? Tenter d'expliquer pourquoi ? | ||
- | |||
- | |||
- | ===== Question 3 : ===== | ||
- | |||
- | Il faut imposer que l'évaluation se fasse à la compilation. Pour ce faire, nous pouvons forcer à ce que l'expression soit évaluée en ajoutant l'attribue ''constexpr'' à la variable résultat du calcul. | ||
- | |||
- | Ainsi le code suivant : | ||
+ | <hidden Correction> | ||
<code cpp> | <code cpp> | ||
- | constexpr long double factorial(int n) | + | void display(std::future<std::string>& aFutureValue, int theDecimals) |
{ | { | ||
- | return n == 0 ? 1 : n * factorial(n - 1); | + | aFutureValue.wait(); |
+ | std::cout << "e with " << theDecimals << " decimals\n" << aFutureValue.get() << std::endl; | ||
} | } | ||
int main() | int main() | ||
{ | { | ||
- | auto res = factorial(100); | + | std::future<std::string> eWidth20000 = std::async(std::launch::async, &computeE, 20000); |
- | std::cout << res << "\n"; | + | std::future<std::string> eWidth100000 = std::async(std::launch::async, &computeE, 100000); |
+ | display(eWidth20000, 20000); | ||
+ | display(eWidth100000, 100000); | ||
} | } | ||
</code> | </code> | ||
+ | </hidden> | ||
- | Indique que le compilateur peut effectué le calcul au moment de la compilation mais la plupart des compilateurs ne le font que partiellement. | ||
- | Pour forcer, il est possible de déclarer la variable ''res'' comme étant une variable stockant le résultat d'une expression constante : | + | ===== Question n°3 ===== |
- | + | ||
- | <code cpp> | + | |
- | int main() | + | |
- | { | + | |
- | auto res = factorial(100); | + | |
- | std::cout << res << "\n"; | + | |
- | } | + | |
- | </code> | + | |
- | + | ||
- | Dans ce cas, le compilateur va lancer l'évaluation de ''factorial(100)'' au moment de la compilation, en effet, il doit s'assurer que ''res'' est une variable stockant le résultat d'une expression constante, le seul moyen de le vérifier est de calculer le résulat. | + | |
- | + | ||
- | Ainsi on force bien l'évaluation au moment de l'exécution. | + | |
- | + | ||
- | Modifier le code des ''lambda'' expressions pour mettre en oeuvre ce mécanisme et estimer les temps de calculs. | + | |
+ | Lancer deux calculs asynchrones, l’un calculant les 1000 premières décimales, l’autre les 10000 premières décimales et afficher les résultats dès que ceux-ci sont disponibles. | ||
<hidden Correction> | <hidden Correction> | ||
- | auto c_factorial_100 = []() { constexpr auto res = factorial(100); return res; }; | + | La fonction ''main'' précédente effectue déjà ce calcul. |
- | fn = estimate_function_time(c_factorial_100); | + | |
- | std::cout << "Computing fact(100)=" << fn.second << " in " << fn.first.count() << " ticks.\n"; | + | |
- | + | ||
- | auto c_power_10002_100000 = []() { constexpr auto res = power_by_int<long double>(1.0002, 100000); return res; }; | + | |
- | pw = estimate_function_time(power_10002_100000); | + | |
- | std::cout << "Computing 1.02^1000000=" << pw.second << " in " << pw.first.count() << " ticks.\n"; | + | |
</hidden> | </hidden> | ||
- | |||
- | |||
- | ===== Question 4 : ===== | ||
- | |||
- | Tester le code suivant avec un compilateur C++20: | ||
- | |||
- | <code cpp> | ||
- | template<double value, int power> | ||
- | struct constant_value | ||
- | { | ||
- | static constexpr long double constant() { constexpr auto result = power_by_int(value, power); return result; } | ||
- | }; | ||
- | |||
- | </code> | ||
- | |||
- | et appeller cette fonction comme suit: | ||
- | |||
- | <code cpp> | ||
- | auto pw_c = estimate_function_time(pre_computed_value<1.0002, 100000>::pow); | ||
- | std::cout << "Computing 1.02^1000000=" << pw_c.second << " in " << pw_c.first.count() << " ticks.\n"; | ||
- | </code> | ||
- | |||
- | Expliquer ce qui se passe ? Et pourquoi ce résultat. | ||
- | |||
- | |||
- | |||
- | |||