User Tools

Site Tools


in204:tds:sujets:td9:part1

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
in204:tds:sujets:td9:part1 [2021/11/06 14:44]
bmonsuez
in204:tds:sujets:td9:part1 [2022/11/18 10:49] (current)
Line 1: Line 1:
-====== ​Partie 1 : Manipuler un ensemble d'​arguments ​======+====== ​Création & Manipulation de Processus léger ​======
  
-===== Question 1 =====+[[in204:​tds:​sujets:​td9|TD9]]
  
-Ecrire une fonction ''​print''​ qui prend un nombre libre d'​entiers en paramètres et qui affiche ces entiers sur la console.+===== Références=====
  
-Pour ce faire, nous vous conseillons d'​autiliser la classe ''​std::initializer_list<​T>''​ comme argument de la fonction.+[[http://​en.cppreference.com/​w/​cpp/​thread/​thread|std::thread]]
  
-Un [[https://​en.cppreference.com/​w/​cpp/​utility/​initializer_list|std::​initializer_list<​T>​]] permet d'​accéder ​à la une liste de valeurs de type ''​T''​ qui est écrit sous la forme suivante :+===== Question n°1 ===== 
 + 
 +Créer un processus léger qui est associé ​à une fonction simple.
  
 <code cpp> <code cpp>
-std::​initializer_list<intlist_of_values = {1, 3, 4, 2 }; +#include<iostream
-</code>+#include<thread>
  
-Cette liste de valeur n'est pas modifiable. Il est seulement possible de la lire en lecture comme un containeur classique. Les fonctions ''​begin()'',​ ''​end()''​ ainsi que ''​size()''​ permettent d'​accéder aux valeurs stockées dans la liste.+void simple_method() 
 +
 + int i = 5; 
 + int x = 10; 
 + int result = i * x; 
 + std::cout << "This code calculated the value " 
 + << result << " from thread ID: "  
 + << std::​this_thread::​get_id() << "​\n";​ 
 +}
  
-<hidden Correction>​ 
  
-<code cpp> +int main()
-void print(std::​initializer_list<​int> arguments)+
 { {
-    auto it = arguments.begin(),​ + std::​thread simpleThread(&​simple_method); 
-        end_it = arguments.end(); + std::cout << ​"Main thread is executing and waiting\n"​
-    if (it != end_it) + simpleThread.join(); 
-    { + std::cout << "Alternate thread has terminated.\n"; 
-        ​std::cout << ​*it+ return 0;
-        ​while ​(++it != end_it+
-            std::cout << "" ​<< *it        +
-    }+
 } }
 +
 </​code>​ </​code>​
  
-</hidden>+Exécuter le code et analyser la sortie. Commenter celle-ci, notamment au regard de la documentation de la classe [[http://​en.cppreference.com/​w/​cpp/​thread/​thread|std::​thread]].
  
-===== Question 2 =====+<hidden Correction>​
  
-Généraliser cette fonction ​à d'autres types que les types entiers+Un objet ''​std::​thread''​ correspondant ​à un processus léger est créé et est associé au code la méthode ​''​simple_method''​. Ce processus démarre immédiatement et lance le calcul. L'​exécution du code principal se poursuit et le message ''​Main thread is executing and waiting''​ est affiché. La ligne suivant attend ​que l'​exécution du processus léger associé à l'​objet ''​std::​thread''​ termine. Une fois que ce processus a terminé, l'​exécution du processus principal continue, affiche le message ''​Alternate thread has terminated''​ et rend la main.
  
 +</​hidden>​
  
-Cette fois-ci, nous attaquons les choses un peu plus complexes. En effet, pour cela, vous allez utiliser les [[https://​en.cppreference.com/​w/​cpp/​language/​parameter_pack|packs de paramètres]] des fonctions et des classes templatées. 
  
-Quand nous écrivons :+===== Question n°2 =====
  
-  * ''​template<​class ...Args> f(Args... arguments)'',​ cela signifie que la fonction prend comme arguments ​un ensemble d'​arguments ​qui ont des types différents. La variable ''​arguments''​ fait référence à l'​ensemble des valeurs ayant chacun ​un type différentc'est pour cela que l'on parle de **pack de paramètres**. Attention, il n'est pas possible d'​accéder aux données individuelles présentes ​dans le **pack de paramètres**.+Ecrire ​un programme ​qui lance les deux calculs suivants en parallèle, le premier dans un processus léger secondairele premier ​dans le processus léger principal :
  
-  * l'​opérateur ''​sizeof...(Args)''​ permet d'​obtenir le nombres d'​arguments passés à la fonction ou spécifié comme paramètres de la classe. 
- 
-  * En fait, les packs de paramètres servent à être passés directement à une fonction. Ainsi nous pouvons écrire le code suivant : 
 <code cpp> <code cpp>
-void number_of_args() { std::cout << "no arguments";​ } +void worker_process(int numberOfIterations)
-template<​classT>​ void number_of_args(T) { std::cout << "one argument";​ } +
-template<​classT1,​ class T2> void number_of_args(T1,​ T2) { std::cout << "two arguments";​ } +
- +
-template<​class ...Args>​ +
-void func(Args... arguments)+
 { {
-    return number_of_args(arguments...);+ for (int i = 1; i < numberOfIterations;​ i++) 
 +
 + std::cout << "​Worker Thread: " << ​ i << "​\n"​; 
 + }
 } }
 </​code>​ </​code>​
-va appeller en fonction du nombre d'​arguments l'une des fonctions ''​number_of_args''​. 
- 
-Nous pouvons désormais imaginer une approche récursive pour compter le nombre d'​arguments en implantant le code suivant : 
  
 +et
 <code cpp> <code cpp>
-template<​class ...Args>​ +void main_process()
-int number_of_args(T first_argument,​ Args... arguments)+
 { {
-    return 0; + for (int i = 11000; i++
-+
-template<class T, class ...Args>​ + std::cout << "​Primary Thread: " << i << "​\n"​; 
-int number_of_args(T first_argument,​ Args... arguments+ }
-+
-    ​return 1 + number_of_args(arguments);+
 } }
 </​code>​ </​code>​
- +  
-L'​appel de la fonction ''​number_of_args''​ qui suit : +Tester ​le code. 
-<code cpp> +
-    std::cout << number_of_args(1,​ "​e",​ 2.0, '​c'​) << "​\n";​ +
-</​code>​ +
- +
-retournera ''​4''​. +
-     +
-==== Question 2.1 ==== +
- +
-Expliquer comment ​le code a été exécuté ? +
- +
- +
-==== Question 2.2 ==== +
- +
-En vous inspirant du code précédant,​ proposer une fonction ''​print''​ qui prend un nombre variable de paramètres qui peuvent avoir des types différents et qui imprime cette liste de paramètres sur la console+
 <hidden Correction>​ <hidden Correction>​
  
-template<​class T> +Nous pouvons écrire le code suivant qui crée deux processus légers, le premier exécutant la fonction ''​worker_process''​ avec comme paramètre ''​10000''​ et le second exécutant la fonction ''​main_proc''​.
-void print(T first_argument) +
-+
-    std::cout << first_argument;​ +
-}+
  
-template<class T, class ...Args+<code cpp
-void print(T first_argument,​ Args... arguments)+int main()
 { {
-    ​std::cout << first_argument;​ + std::thread worker_proc(&​worker_process,​ 10000); 
-    if (sizeof...(Args) > 0+ std::thread main_proc(&​main_process)
-        std::cout << ", "+ worker_proc.join(); 
-    print(arguments...);+ main_proc.join();
 } }
 +</​code>​
  
-</​hidden>​+L'​exécution commence par la tâche ''​worker_proc''​ et nous devons voir certains messages de la tâche ''​main_proc'',​ nous voyons cependant que les messages ne sont pas très entrelacés,​ ce qui est du au fait que l'on alloue un temps d'​utilisation de la console à chacun des deux processus légers.
  
 +Pour mettre un peu d'​entrelacement,​ nous pouvons ajouter un peu de temps entre les affichaches,​ par exemple 1ns pour le premier processus et 10ns pour le second. Ceci nous donne le code suivant:
  
-====== Partie 2 :Mesuré le temps passé par une fonction ======+<code cpp> 
 +#​include<​iostream>​ 
 +#​include<​thread>​ 
 +#include <​chrono>​
  
-[[in204:​tds:​sujets:​td9|TD9]] 
  
-===== Références===== 
  
-[[http://​en.cppreference.com/​w/​cpp/​thread/​thread|std::​thread]] +void worker_process(int numberOfIterations)
- +
- +
-====== 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 <= 1 ? 1 (* factorial(n - 1));+ for (int i = 1; i < numberOfIterations;​ i++) 
 +
 + std::cout << "​Worker Thread: " << i << "\n"; 
 + std::​this_thread::​sleep_for(10ns); 
 + }
 } }
-</​code>​ 
  
-La fonction aura le squelette suivant: +void main_process()
-<​code>​ +
-int estimate_time(int n)+
 { {
-    // Code pour lancer le chronomètre + for (int 1; i < 1000; i++) 
-    ​int result ​factorial(n); +
-    // Code pour calculer le temps écoulé et affiché celui-ci. + using namespace std
-    ​return result;+ std::cout << "​Primary Thread: " << i << "​\n";​ 
 + std::​this_thread::​sleep_for(1ns); 
 + }
 } }
-</​code>​ 
  
-===== Question n°2 ===== +int main()
- +
-Transformer la fonction précédente <​code>​estimate_time</​code>​ pour qu'​elle prenne en argument une fonction arbitraire à un argument et un résultat.  +
- +
-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> +
-std::​pair<​std::​chrono::​high_resolution_clock::​duration,​ long double> estimate_function_time(Function function, T argument)+
 { {
- auto starting_time = std::chrono::​high_resolution_clock::​now(); + std::thread worker_proc(&​worker_process,​ 10000); 
- auto result = function(argument); + std::thread main_proc(&​main_process); 
- auto elasped_time = std::​chrono::​high_resolution_clock::​now() - starting_time+ worker_proc.join(); 
- return std::​make_pair(elasped_time,​ result);+ main_proc.join();
 } }
 </​code>​ </​code>​
  
 </​hidden>​ </​hidden>​
- 
- 
-===== Question n°3 ===== 
- 
-Nous souhaitons désormais pouvoir prendre une fonction pouvant prendre plusieurs arguments comme la fonction puissance. 
- 
-<code cpp> 
-template<​class T1, T2> ​ 
-T1 power(T2 x, int y) 
-{ 
-   T1 result = (T1)1.0 
-   ​while(y-- > 0) 
-       ​result ​ = result * (T1)x; 
-   ​return result; 
-} 
-</​code>​ 
- 
-Modifier le code de la fonction pour pouvoir prendre une telle fonction comme paramètre. 
- 
-Estimer le temps nécesaire pour calculer par exemple : 1.02 ^ 10000000. 
- 
- 
-==== Question n°5 ==== 
- 
-Créer une fonction nouvelle qui va appeller la fonction <​code>​estimate_time</​code>​ pour calculer <​code>​x</​code>​ fois le temps et retourné le temps moyen pour effectuer un "​run"​ de la fonction. 
- 
-Exécuter cette fonction pour les fonctions <​code>​power</​code>​ et <​code>​factorial</​code>​ précédemment définie. 
- 
-==== Question n°4 ==== 
- 
-Que faut-il faire ajouter pour dire au compilateur que la fonction <​code>​factorial</​code>​ ou que la fonction <​code>​power</​code>​ peut-être exécutée au moment de l'​exécution ? 
- 
-Effectuer la modification. 
- 
-Calculer le temps pris désormais par ces fonctions. 
- 
- 
  
  
in204/tds/sujets/td9/part1.1636209850.txt.gz · Last modified: 2021/11/06 14:44 by bmonsuez