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:part1 [2021/11/06 12:32] 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 <code>print</code> 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 <code>std::initializer_list<T></code> 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 <code>T</code> 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<int> list_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 <code>begin()</code>, <code>end()</code> ainsi que <code>size()</code> 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. |
- | |||
- | 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 : | ||
- | |||
- | * <code>template<class ...Args> f(Args... arguments)</code>, cela signifie que la fonction prend comme arguments un ensemble d'arguments qui ont des types différents. La variable <code>arguments</code> fait référence à l'ensemble des valeurs ayant chacun un type différent. | ||
- | |||
- | |||
- | |||
- | <hidden Correction> | ||
</hidden> | </hidden> | ||
- | ====== Partie 2 :Mesuré le temps passé par une fonction ====== | + | ===== Question n°2 ===== |
- | + | ||
- | [[in204:tds:sujets:td9|TD9]] | + | |
- | + | ||
- | ===== Références===== | + | |
- | + | ||
- | [[http://en.cppreference.com/w/cpp/thread/thread|std::thread]] | + | |
- | + | ||
- | + | ||
- | ====== 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: | + | Ecrire un programme qui lance les deux calculs suivants en parallèle, le premier dans un processus léger secondaire, le premier dans le processus léger principal : |
- | et qui va exécuter cette fonction | + | |
<code cpp> | <code cpp> | ||
- | int factorial(int n) | + | void worker_process(int numberOfIterations) |
{ | { | ||
- | return n <= 1 ? 1 : (n * factorial(n - 1)); | + | for (int i = 1; i < numberOfIterations; i++) |
+ | { | ||
+ | std::cout << "Worker Thread: " << i << "\n"; | ||
+ | } | ||
} | } | ||
</code> | </code> | ||
- | La fonction aura le squelette suivant: | + | et |
- | <code> | + | <code cpp> |
- | int estimate_time(int n) | + | void main_process() |
{ | { | ||
- | // Code pour lancer le chronomètre | + | for (int i = 1; i < 1000; i++) |
- | int result = factorial(n); | + | { |
- | // Code pour calculer le temps écoulé et affiché celui-ci. | + | std::cout << "Primary Thread: " << i << "\n"; |
- | return result; | + | } |
} | } | ||
</code> | </code> | ||
- | + | ||
- | ===== Question n°2 ===== | + | Tester le code. |
- | + | ||
- | 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> | <hidden Correction> | ||
- | Il suffit de modifier la fonction antérieure comme un fonction qui prend deux paramètres de types: | + | 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''. |
- | - 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> | <code cpp> | ||
- | template<class Function, class T> | + | int main() |
- | 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> | + | 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: | ||
- | ===== Question n°3 ===== | + | <code cpp> |
+ | #include<iostream> | ||
+ | #include<thread> | ||
+ | #include <chrono> | ||
- | Nous souhaitons désormais pouvoir prendre une fonction pouvant prendre plusieurs arguments comme la fonction puissance. | ||
- | <code cpp> | + | |
- | template<class T1, T2> | + | void worker_process(int numberOfIterations) |
- | T1 power(T2 x, int y) | + | |
{ | { | ||
- | T1 result = (T1)1.0 | + | for (int i = 1; i < numberOfIterations; i++) |
- | while(y-- > 0) | + | { |
- | result = result * (T1)x; | + | std::cout << "Worker Thread: " << i << "\n"; |
- | return result; | + | std::this_thread::sleep_for(10ns); |
+ | } | ||
} | } | ||
- | </code> | ||
- | Modifier le code de la fonction pour pouvoir prendre une telle fonction comme paramètre. | + | void main_process() |
- | + | { | |
- | Estimer le temps nécesaire pour calculer par exemple : 1.02 ^ 10000000. | + | for (int i = 1; i < 1000; i++) |
- | + | { | |
- | + | using namespace std; | |
- | ==== Question n°5 ==== | + | std::cout << "Primary Thread: " << i << "\n"; |
- | + | std::this_thread::sleep_for(1ns); | |
- | 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. | + | |
+ | int main() | ||
+ | { | ||
+ | std::thread worker_proc(&worker_process, 10000); | ||
+ | std::thread main_proc(&main_process); | ||
+ | worker_proc.join(); | ||
+ | main_proc.join(); | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||