🧩 Chapitre 1 – Informatique parallèle (Exercices pratiques
Python)
⚙️Exercice 1 : Comprendre la différence entre séquentiel et parallèle
Objectif : mesurer le gain de temps entre un calcul séquentiel et un calcul parallèle.
# Exercice 1 : Comparaison séquentielle vs parallèle
# ---------------------------------------------------
# Objectif : calculer la somme des carrés d'une grande liste
# de manière séquentielle puis parallèle pour observer la différence de
performance.
import multiprocessing
import time
# --- Fonction globale (doit être définie ici, hors du main) ---
def somme_carres(liste):
"""Calcule la somme des carrés de la liste fournie"""
return sum(x**2 for x in liste)
# --- Programme principal ---
def main():
data = list(range(10_000_000))
print("=== Comparaison séquentielle vs parallèle ===")
# Mode séquentiel
t1 = [Link]()
result_seq = somme_carres(data)
t2 = [Link]()
print(f"Résultat séquentiel = {result_seq}")
print(f"Temps séquentiel : {t2 - t1:.4f} secondes")
# Mode parallèle
nb_process = multiprocessing.cpu_count()
print(f"\nNombre de cœurs détectés : {nb_process}")
taille_bloc = len(data) // nb_process
sous_listes = [data[i*taille_bloc:(i+1)*taille_bloc] for i in
range(nb_process)]
t3 = [Link]()
with [Link](nb_process) as pool:
resultats = [Link](somme_carres, sous_listes)
result_par = sum(resultats)
t4 = [Link]()
print(f"Résultat parallèle = {result_par}")
print(f"Temps parallèle : {t4 - t3:.4f} secondes")
print(f"Accélération (speedup) = {(t2 - t1) / (t4 - t3):.2f}x")
if __name__ == "__main__":
multiprocessing.freeze_support() # utile pour Windows/Thonny
main()
🧠 Analyse :
Tu verras que le code parallèle utilise plusieurs cœurs du CPU.
Le gain dépend de la taille du problème et du nombre de cœurs disponibles.
=== Comparaison séquentielle vs parallèle ===
Résultat séquentiel = 333333283333335000000
Temps séquentiel : 1.8567 secondes
Nombre de cœurs détectés : 12
Résultat parallèle = 333332883333534999970
Temps parallèle : 1.2162 secondes
Accélération (speedup) = 1.53x
🔢 Exercice 2 : Visualiser la scalabilité
Objectif : mesurer le temps d’exécution selon le nombre de processus utilisés.
# Exercice 2 : Étude de la scalabilité
# -------------------------------------
import multiprocessing
import time
def travail_intensif(n):
# Calcule des opérations CPU-bound pour simuler du calcul lourd
total = 0
for i in range(1_000_000):
total += i ** 2
return total
if __name__ == "__main__":
for nb_proc in [1, 2, 4, 8]:
t1 = [Link]()
with [Link](nb_proc) as pool:
[Link](travail_intensif, range(nb_proc))
t2 = [Link]()
print(f"{nb_proc} processus -> Temps : {t2 - t1:.3f} sec")
🧠 Analyse :
Observe la diminution du temps lorsque tu augmentes le nombre de processus.
Tu peux calculer le speedup = T(1)/T(N) pour chaque cas.
Au-delà d’un certain nombre, le gain devient faible → saturation du parallélisme.
>>> %Run mesure_temp.py
1 processus -> Temps : 0.278 sec
2 processus -> Temps : 0.310 sec
4 processus -> Temps : 0.459 sec
8 processus -> Temps : 0.519 sec
⚡ Exercice 3 : Mesure de l’efficacité énergétique (approximation)
Objectif : estimer l’efficacité du calcul parallèle (CPU usage / gain obtenu).
# Exercice 3 : Estimation de l’efficacité énergétique
# ----------------------------------------------------
import multiprocessing
import time
import psutil # pip install psutil si nécessaire
# --- Fonction calcul ---
def calcul(dummy): # ajouter un argument dummy pour map
s = 0
for i in range(2_000_000):
s += i ** 2
return s
if __name__ == "__main__":
multiprocessing.freeze_support() # important pour Windows
for n in [1, 2, 4]:
cpu_before = psutil.cpu_percent(interval=0.1)
start = [Link]()
# On crée un pool de n processus et on exécute n tâches
with [Link](n) as p:
[Link](calcul, range(n)) # range(n) fournit n arguments dummy
end = [Link]()
cpu_after = psutil.cpu_percent(interval=0.1)
print(f"{n} processus -> Temps : {end - start:.2f}s | Charge CPU :
{cpu_after}%")
💡 Idée :
Compare la charge CPU et le temps.
Tu comprendras pourquoi plus de cœurs ≠ toujours plus efficace.
>>> %Run efficacity_energy.py
1 processus -> Temps : 0.47s | Charge CPU : 8.8%
2 processus -> Temps : 0.52s | Charge CPU : 4.4%
4 processus -> Temps : 0.99s | Charge CPU : 19.7%
>>>
🧠 Exercice 4 : Historique – Simulation d’évolution
Objectif : montrer l’effet du passage d’un CPU mono-cœur à multi-cœur.
# Exercice 4 : Simulation historique
# -----------------------------------
# On simule le temps de calcul pour différentes générations de CPU
import [Link] as plt
# Hypothèses (temps pour une tâche donnée)
cpu_generations = {
"Mono-cœur": 10.0,
"Bi-cœur": 5.5,
"Quadri-cœur": 3.0,
"Octo-cœur": 2.0,
"Multi-cœur (16)": 1.5,
}
[Link](cpu_generations.keys(), cpu_generations.values(), color='skyblue')
[Link]("Temps d'exécution (s)")
[Link]("Évolution historique des architectures multi-cœurs")
[Link]()
💬 Conclusion :
Le gain vient de la parallélisation du matériel (multi-cœurs).
L’informatique parallèle s’est démocratisée grâce à cette évolution.
🧮 Exercice 5 : Domaines d’application
Objectif : montrer la diversité des domaines exploitant le parallèle.
# Exercice 5 : Domaines d’application simulés
# -------------------------------------------
applications = {
"Simulation scientifique": "Résolution d'équations différentielles
(météo, physique)",
"Industrie": "Simulation de flux, conception CAO",
"Intelligence artificielle": "Entraînement des réseaux neuronaux (GPU,
TPU)",
"Big Data": "Traitement distribué Hadoop, Spark",
"Finance": "Calculs Monte-Carlo, prévisions de risque"
}
print("Domaines utilisant l'informatique parallèle :\n")
for domaine, usage in [Link]():
print(f"- {domaine}: {usage}")