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:12] bmonsuez [Question 1 :] |
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 : | ||
| + | <hidden Correction> | ||
| <code cpp> | <code cpp> | ||
| - | factorial_100 = []() { return factorial(100); }; | + | void display(std::future<std::string>& aFutureValue, int theDecimals) |
| - | fn = estimate_function_time(factorial_100); | + | { |
| - | std::cout << "Computing fact(100)=" << fn.second << " in " << fn.first.count() << " ticks.\n"; | + | aFutureValue.wait(); |
| - | + | std::cout << "e with " << theDecimals << " decimals\n" << aFutureValue.get() << std::endl; | |
| - | 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> | + | |
| - | + | int main() | |
| - | Est-ce que cela améliore les résultats ? Tenter d'expliquer pourquoi ? | + | |
| - | + | ||
| - | + | ||
| - | </code> | + | |
| - | ===== Question 2 : ===== | + | |
| - | + | ||
| - | 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; } | + | std::future<std::string> eWidth20000 = std::async(std::launch::async, &computeE, 20000); |
| - | }; | + | std::future<std::string> eWidth100000 = std::async(std::launch::async, &computeE, 100000); |
| + | display(eWidth20000, 20000); | ||
| + | display(eWidth100000, 100000); | ||
| + | } | ||
| </code> | </code> | ||
| - | + | </hidden> | |
| - | 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. | + | |
| + | ===== Question n°3 ===== | ||
| + | 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> | ||
| + | La fonction ''main'' précédente effectue déjà ce calcul. | ||
| + | </hidden> | ||