first comit
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
# --- Configuration ---
|
||||
NUM_TASKS = 15
|
||||
SLEEP_DURATION = 0.2 # Chaque tâche "attendra" 0.2 seconde
|
||||
|
||||
|
||||
# --- Fonctions ---
|
||||
def setup_logging():
|
||||
"""Configure un logging simple pour la sortie console."""
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.StreamHandler(sys.stdout)],
|
||||
)
|
||||
|
||||
|
||||
def worker_task(task_id: int):
|
||||
"""
|
||||
Une fonction qui simule un travail bloquant (ex: une longue requête réseau).
|
||||
time.sleep() gèle le thread et bloque l'exécution de tout autre code.
|
||||
"""
|
||||
logging.info(f"Tâche {task_id}: Démarrage...")
|
||||
time.sleep(SLEEP_DURATION)
|
||||
logging.info(f"Tâche {task_id}: Terminée.")
|
||||
return f"Résultat de la tâche {task_id}"
|
||||
|
||||
|
||||
def main():
|
||||
"""Fonction principale du script synchrone."""
|
||||
setup_logging()
|
||||
logging.info("--- DÉMARRAGE DE LA SIMULATION SYNCHRONE ---")
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# Les tâches sont exécutées l'une après l'autre.
|
||||
# La suivante ne commence que lorsque la précédente est terminée.
|
||||
results = [worker_task(i) for i in range(NUM_TASKS)]
|
||||
|
||||
duration = time.perf_counter() - start_time
|
||||
|
||||
logging.info("-" * 50)
|
||||
logging.info(f"Exécution SYNCHRONE terminée en {duration:.2f} secondes.")
|
||||
|
||||
expected_time = NUM_TASKS * SLEEP_DURATION
|
||||
logging.info(f"Temps attendu : {NUM_TASKS} tâches * {SLEEP_DURATION}s = {expected_time:.2f}s.")
|
||||
logging.info("Les temps d'attente se sont additionnés.")
|
||||
logging.info("-" * 50)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
54
demo_concurrence/1_sync_simulation.py
Normal file
54
demo_concurrence/1_sync_simulation.py
Normal file
@@ -0,0 +1,54 @@
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
# --- Configuration ---
|
||||
NUM_TASKS = 15
|
||||
SLEEP_DURATION = 0.2 # Chaque tâche "attendra" 0.2 seconde
|
||||
|
||||
|
||||
# --- Fonctions ---
|
||||
def setup_logging():
|
||||
"""Configure un logging simple pour la sortie console."""
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.StreamHandler(sys.stdout)],
|
||||
)
|
||||
|
||||
|
||||
def worker_task(task_id: int):
|
||||
"""
|
||||
Une fonction qui simule un travail bloquant (ex: une longue requête réseau).
|
||||
time.sleep() gèle le thread et bloque l'exécution de tout autre code.
|
||||
"""
|
||||
logging.info(f"Tâche {task_id}: Démarrage...")
|
||||
time.sleep(SLEEP_DURATION)
|
||||
logging.info(f"Tâche {task_id}: Terminée.")
|
||||
return f"Résultat de la tâche {task_id}"
|
||||
|
||||
|
||||
def main():
|
||||
"""Fonction principale du script synchrone."""
|
||||
setup_logging()
|
||||
logging.info("--- DÉMARRAGE DE LA SIMULATION SYNCHRONE ---")
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# Les tâches sont exécutées l'une après l'autre.
|
||||
# La suivante ne commence que lorsque la précédente est terminée.
|
||||
results = [worker_task(i) for i in range(NUM_TASKS)]
|
||||
|
||||
duration = time.perf_counter() - start_time
|
||||
|
||||
logging.info("-" * 50)
|
||||
logging.info(f"Exécution SYNCHRONE terminée en {duration:.2f} secondes.")
|
||||
|
||||
expected_time = NUM_TASKS * SLEEP_DURATION
|
||||
logging.info(f"Temps attendu : {NUM_TASKS} tâches * {SLEEP_DURATION}s = {expected_time:.2f}s.")
|
||||
logging.info("Les temps d'attente se sont additionnés.")
|
||||
logging.info("-" * 50)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
56
demo_concurrence/2_async_simulation.py
Normal file
56
demo_concurrence/2_async_simulation.py
Normal file
@@ -0,0 +1,56 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
import time
|
||||
|
||||
# --- Configuration ---
|
||||
NUM_TASKS = 15
|
||||
SLEEP_DURATION = 0.2 # Chaque tâche "attendra" 0.2 seconde
|
||||
|
||||
|
||||
# --- Fonctions ---
|
||||
def setup_logging():
|
||||
"""Configure un logging simple pour la sortie console."""
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(levelname)s - %(message)s",
|
||||
handlers=[logging.StreamHandler(sys.stdout)],
|
||||
)
|
||||
|
||||
|
||||
# TODO 1 : remplacer "def" par "async def"
|
||||
def worker_task_async(task_id: int):
|
||||
"""
|
||||
Une coroutine qui simule un travail non-bloquant.
|
||||
await asyncio.sleep() ne gèle pas le programme. Il notifie la boucle
|
||||
d'événements qu'elle peut exécuter autre chose pendant cette pause.
|
||||
"""
|
||||
logging.info(f"Tâche {task_id}: Démarrage...")
|
||||
|
||||
# TODO 2 : remplacer "time.sleep" par "await asyncio.sleep"
|
||||
|
||||
logging.info(f"Tâche {task_id}: Terminée.")
|
||||
return f"Résultat de la tâche {task_id}"
|
||||
|
||||
|
||||
async def main():
|
||||
"""Fonction principale (coroutine) du script asynchrone."""
|
||||
setup_logging()
|
||||
logging.info("--- DÉMARRAGE DE LA SIMULATION ASYNCHRONE ---")
|
||||
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# TODO 3 : créer une liste de tâches en appelant worker_task_async, puis utiliser asyncio.gather
|
||||
|
||||
|
||||
duration = time.perf_counter() - start_time
|
||||
|
||||
logging.info("-" * 50)
|
||||
logging.info(f"Exécution ASYNCHRONE terminée en {duration:.2f} secondes.")
|
||||
logging.info(f"Temps attendu : proche de la durée d'une seule tâche ({SLEEP_DURATION}s).")
|
||||
logging.info("Les temps d'attente ont été gérés en parallèle.")
|
||||
logging.info("-" * 50)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
105
demo_concurrence/3_multiprocessing_demo.py
Normal file
105
demo_concurrence/3_multiprocessing_demo.py
Normal file
@@ -0,0 +1,105 @@
|
||||
import logging
|
||||
import multiprocessing
|
||||
import sys
|
||||
import time
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
from typing import List, Tuple, Any, Callable
|
||||
|
||||
# --- CONFIGURATION ---
|
||||
# Le nombre de tâches de calcul à simuler.
|
||||
# Augmentez ce nombre pour mieux voir la différence de performance.
|
||||
NUM_TASKS = 20
|
||||
|
||||
# --- LE WORKER (la tâche CPU-Bound) ---
|
||||
|
||||
def heavy_analysis(args: Tuple[int, int]) -> Tuple[int, float]:
|
||||
"""
|
||||
Une fonction qui simule un calcul scientifique ou une analyse de données complexe.
|
||||
C'est une tâche purement "CPU-bound".
|
||||
|
||||
Elle prend un tuple en argument pour s'aligner sur le pattern utilisé dans le TP, que vous traiterez plus tard.
|
||||
|
||||
Paramètres:
|
||||
args (Tuple[int, int]): Un tuple contenant:
|
||||
- task_id (int): L'identifiant de la tâche.
|
||||
- complexity (int): Un paramètre pour faire varier la durée du calcul.
|
||||
|
||||
Retourne:
|
||||
Tuple[int, float]: Un tuple avec l'ID de la tâche et le résultat du calcul.
|
||||
"""
|
||||
task_id, complexity = args
|
||||
logging.info(f"Tâche {task_id}: Démarrage du calcul (complexité={complexity})...")
|
||||
|
||||
# Simulation d'un calcul intensif : une boucle qui fait des opérations mathématiques.
|
||||
result = 0
|
||||
# La limite de la boucle dépend de la complexité pour que les tâches n'aient pas toutes la même durée.
|
||||
limit = 2_000_000 + (complexity * 500_000)
|
||||
for i in range(limit):
|
||||
result += (i ** 0.5) / (i + 1) # Opération mathématique arbitraire
|
||||
|
||||
logging.info(f"Tâche {task_id}: Calcul terminé.")
|
||||
return (task_id, result)
|
||||
|
||||
# --- LES LANCEURS ---
|
||||
|
||||
def run_sequential(worker_function: Callable, tasks: List[Any]) -> float:
|
||||
"""Exécute les tâches de manière séquentielle."""
|
||||
logging.info("--- DÉMARRAGE DU MODE SÉQUENTIEL ---")
|
||||
start_time = time.perf_counter()
|
||||
|
||||
results = [worker_function(task) for task in tasks]
|
||||
|
||||
duration = time.perf_counter() - start_time
|
||||
logging.info(f"--- MODE SÉQUENTIEL terminé en {duration:.2f} secondes. ---\n")
|
||||
return duration
|
||||
|
||||
def run_parallel(worker_function: Callable, tasks: List[Any]) -> float:
|
||||
"""Exécute les tâches en parallèle en utilisant tous les cœurs CPU disponibles."""
|
||||
# On utilise tous les cœurs disponibles pour maximiser le parallélisme.
|
||||
worker_count = multiprocessing.cpu_count()
|
||||
logging.info(f"--- DÉMARRAGE DU MODE PARALLÈLE (avec {worker_count} processus) ---")
|
||||
start_time = time.perf_counter()
|
||||
|
||||
# TODO 1 : instancier un ProcessPoolExecutor avec un nombre de workers donné, et en utilisant le gestionnaire de contexte `with`.
|
||||
|
||||
# TODO 2 : utiliser la méthode `map` de l'executor pour exécuter `worker_function` sur chaque tâche dans `tasks`.
|
||||
|
||||
duration = time.perf_counter() - start_time
|
||||
logging.info(f"--- MODE PARALLÈLE terminé en {duration:.2f} secondes. ---\n")
|
||||
return duration
|
||||
|
||||
# --- FONCTION PRINCIPALE ---
|
||||
|
||||
def main():
|
||||
"""Orchestre la démonstration."""
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format="%(asctime)s - %(processName)s - %(message)s",
|
||||
handlers=[logging.StreamHandler(sys.stdout)],
|
||||
)
|
||||
|
||||
# Création de la liste de tâches à exécuter.
|
||||
# Chaque tâche est un tuple d'arguments pour notre fonction `heavy_analysis`.
|
||||
# On fait varier la complexité pour simuler des données hétérogènes.
|
||||
tasks_to_run = [(i, (i % 5) + 1) for i in range(NUM_TASKS)]
|
||||
logging.info(f"{len(tasks_to_run)} tâches de calcul vont être lancées.")
|
||||
|
||||
# Lancement séquentiel
|
||||
seq_duration = run_sequential(heavy_analysis, tasks_to_run)
|
||||
|
||||
# Lancement parallèle
|
||||
par_duration = run_parallel(heavy_analysis, tasks_to_run)
|
||||
|
||||
# Résumé
|
||||
logging.info("--- RÉSUMÉ DE LA COMPARAISON ---")
|
||||
logging.info(f"Temps d'exécution séquentiel : {seq_duration:.2f}s")
|
||||
logging.info(f"Temps d'exécution parallèle : {par_duration:.2f}s")
|
||||
if par_duration > 0:
|
||||
speedup = seq_duration / par_duration
|
||||
logging.info(f"Facteur d'accélération (Speedup) : {speedup:.2f}x")
|
||||
logging.info("----------------------------------")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Nécessaire pour que multiprocessing fonctionne correctement sur certaines plateformes (Windows)
|
||||
multiprocessing.freeze_support()
|
||||
main()
|
||||
0
demo_concurrence/__init__.py
Normal file
0
demo_concurrence/__init__.py
Normal file
Reference in New Issue
Block a user