Code
Voici un exemple de calcul distribué avec Julia qui reprend notre problème initial.
using Distributed
# Configuration et initialisation
#-------------------------------
println("Chargement des adresses IP des workers depuis servers_ip.csv...")
file_content = read("servers_ip.csv", String)
ip_list = split(replace(file_content, "\"" => ""), ",")
machines = strip.(ip_list)
worker_addresses = map(ip -> "worker@" * ip, machines)
println("Configuration des workers sur les machines : $(worker_addresses)")
# Ajout des processus workers distants
addprocs(worker_addresses,
exename="/home/worker/.juliaup/bin/julia",
dir="/home/worker",
tunnel=true
)
println("Nombre de workers disponibles : $(nworkers())")
# Définition des fonctions sur tous les workers
#-------------------------------------------
@everywhere begin
function circle_equation(x::Float64, y::Float64)
return (x - 1.0)^2 + (y - 2.0)^2 - 1.0
end
function find_points(x_range, y_range, tolerance)
points = Tuple{Float64, Float64}[]
grid_size = length(x_range) * length(y_range)
worker_id = myid() - 1 # -1 car les workers commencent à 2
println("Worker $worker_id traite une grille de $(length(x_range))×$(length(y_range)) = $grid_size points")
for x in x_range
for y in y_range
if abs(circle_equation(x, y)) < tolerance
push!(points, (x, y))
end
end
end
return points
end
end
# Paramètres du calcul
#--------------------
grid_size = 1000 # Nombre de points par dimension
x_range = range(-10.0, 10.0, length=grid_size)
y_range = range(-10.0, 10.0, length=grid_size)
tolerance = 0.01
# Affichage des informations sur la grille complète
#-----------------------------------------------
total_grid_size = length(x_range) * length(y_range)
println("\nInformations sur la grille complète :")
println("Dimensions : $(length(x_range))×$(length(y_range)) = $total_grid_size points")
println("Plage en x : [$(minimum(x_range)), $(maximum(x_range))]")
println("Plage en y : [$(minimum(y_range)), $(maximum(y_range))]")
println("Tolérance : $tolerance")
# Division du travail entre les workers
#------------------------------------
points_per_worker = div(length(x_range), nworkers())
println("\nRépartition du travail :")
println("Points sur l'axe x par worker : $points_per_worker")
# Création des sous-ensembles pour chaque worker
chunks = []
for worker_id in 1:nworkers()
start_index = (worker_id - 1) * points_per_worker + 1
end_index = min(worker_id * points_per_worker, length(x_range))
worker_points = x_range[start_index:end_index]
push!(chunks, worker_points)
# Calcul de la taille de la grille pour ce worker
worker_grid_size = length(worker_points) * length(y_range)
println("Worker $worker_id : plage x[$(start_index):$(end_index)] = $(length(worker_points)) points")
println(" taille de la sous-grille : $(length(worker_points))×$(length(y_range)) = $worker_grid_size points")
end
# Exécution distribuée
#--------------------
println("\nDémarrage du calcul distribué...")
@time begin
results = @distributed (vcat) for chunk in chunks
find_points(chunk, y_range, tolerance)
end
end
# Affichage des résultats
#-----------------------
println("\nRésultats :")
println("Nombre total de points trouvés : $(length(results))")
if !isempty(results)
println("Premiers points trouvés : $(results[1:min(5, length(results))])")
println("\nVérification des premiers points :")
for (i, (x, y)) in enumerate(results[1:min(5, length(results))])
distance = sqrt((x - 1.0)^2 + (y - 2.0)^2)
println("Point $i : ($x, $y), distance du centre = $distance")
end
end
# Nettoyage
#----------
rmprocs(workers())
println("\nCalcul terminé et workers libérés.")
Explications
1. Vue d’ensemble
Le programme implémente un système de calcul distribué pour trouver les points d’un cercle de rayon 1 centré en (1,2). Il utilise une architecture maître-esclaves où un processus principal coordonne plusieurs workers qui effectuent les calculs en parallèle.
2. Composants principaux
2.1 Initialisation du système
La phase d’initialisation comprend :
- La lecture d’un fichier CSV contenant les adresses IP des machines workers
- Le nettoyage et le formatage des adresses pour la connexion SSH
- L’établissement des connexions avec les machines distantes
- La configuration des paramètres de base (chemins, répertoires)
2.2 Configuration des workers
Chaque worker est configuré avec :
- Un chemin vers l’exécutable Julia
- Un répertoire de travail spécifique
- Un tunnel SSH pour la communication sécurisée
- Les droits et permissions nécessaires
2.3 Fonctions mathématiques
Deux fonctions principales sont distribuées sur tous les workers :
- La fonction d’équation du cercle qui calcule (x-1)² + (y-2)² - 1
- La fonction de recherche qui parcourt une portion de la grille pour trouver les points satisfaisant l’équation
Ces fonctions sont définies avec le décorateur @everywhere
pour être disponibles sur tous les processus.
2.4 Paramètres de calcul
Les paramètres clés incluent :
- La taille de la grille (1000×1000 points)
- L’intervalle de recherche ([-10, 10] sur les deux axes)
- La tolérance acceptée (0.01)
- Le nombre de workers disponibles
2.5 Répartition du travail
Le système répartit le travail selon ces principes :
- Division de la grille en sections égales
- Attribution d’une section à chaque worker
- Monitoring de la charge de travail
- Gestion des limites de grille
2.6 Processus de calcul
Le calcul s’effectue en plusieurs étapes :
- Distribution des portions de grille aux workers
- Exécution parallèle des calculs
- Collecte et fusion des résultats partiels
- Vérification de la validité des résultats
2.7 Gestion des résultats
Le programme gère les résultats en :
- Collectant les points trouvés par chaque worker
- Vérifiant leur validité
- Calculant des statistiques
- Affichant les informations pertinentes
2.8 Finalisation et nettoyage
La finalisation comprend :
- La libération des ressources
- La déconnexion propre des workers
- L’affichage des statistiques finales
- La sauvegarde éventuelle des résultats
3. Aspects techniques importants
3.1 Parallélisation
- Parallélisme de données
- Distribution équitable de la charge
- Minimisation des communications
3.2 Optimisation
- Types de données optimisés
- Gestion efficace de la mémoire
- Réduction des entrées/sorties
3.3 Sécurité
- Tunneling SSH
- Gestion des accès
- Protection des données
3.4 Robustesse
- Gestion des erreurs
- Validation des résultats
- Récupération après échec
3.5 Performance
- Équilibrage de charge
- Optimisation des communications
- Utilisation efficace des ressources
4. Points forts
- Scalabilité : peut fonctionner avec un nombre variable de workers
- Robustesse : gestion des erreurs et des cas limites
- Performance : parallélisation efficace des calculs
- Maintenabilité : code structuré et bien documenté
- Flexibilité : paramètres ajustables selon les besoins
5. Limitations potentielles
- Dépendance à la qualité du réseau
- Nécessité d’une configuration SSH correcte
- Surcharge possible avec trop de workers
- Consommation mémoire sur les grandes grilles
Exécution
Nous pouvons exécuter ce programme et nous obtenons :
deployer@ubuntu-deploy:~/homelab_julia_pve_tf_ansible$ julia distributed_execution.jl
Chargement des adresses IP des workers depuis servers_ip.csv...
Configuration des workers sur les machines : ["worker@192.168.1.201", "worker@192.168.1.202", "worker@192.168.1.203", "worker@192.168.1.204", "worker@192.168.1.205"]
Nombre de workers disponibles : 5
Informations sur la grille complète :
Dimensions : 1000×1000 = 1000000 points
Plage en x : [-10.0, 10.0]
Plage en y : [-10.0, 10.0]
Tolérance : 0.01
Répartition du travail :
Points sur l'axe x par worker : 200
Worker 1 : plage x[1:200] = 200 points
taille de la sous-grille : 200×1000 = 200000 points
Worker 2 : plage x[201:400] = 200 points
taille de la sous-grille : 200×1000 = 200000 points
Worker 3 : plage x[401:600] = 200 points
taille de la sous-grille : 200×1000 = 200000 points
Worker 4 : plage x[601:800] = 200 points
taille de la sous-grille : 200×1000 = 200000 points
Worker 5 : plage x[801:1000] = 200 points
taille de la sous-grille : 200×1000 = 200000 points
Démarrage du calcul distribué...
From worker 6: Worker 5 traite une grille de 200×1000 = 200000 points
From worker 4: Worker 3 traite une grille de 200×1000 = 200000 points
From worker 2: Worker 1 traite une grille de 200×1000 = 200000 points
From worker 3: Worker 2 traite une grille de 200×1000 = 200000 points
From worker 5: Worker 4 traite une grille de 200×1000 = 200000 points
1.648146 seconds (813.16 k allocations: 40.339 MiB, 24.90% compilation time)
Résultats :
Nombre total de points trouvés : 142
Premiers points trouvés : [(0.01001001001001001, 1.8318318318318318), (0.01001001001001001, 1.8518518518518519), (0.01001001001001001, 1.8718718718718719), (0.01001001001001001, 1.8918918918918919), (0.01001001001001001, 2.1121121121121122)]
Vérification des premiers points :
Point 1 : (0.01001001001001001, 1.8318318318318318), distance du centre = 1.0041716551792417
Point 2 : (0.01001001001001001, 1.8518518518518519), distance du centre = 1.0010135134353113
Point 3 : (0.01001001001001001, 1.8718718718718719), distance du centre = 0.9982469621781969
Point 4 : (0.01001001001001001, 1.8918918918918919), distance du centre = 0.9958752649398894
Point 5 : (0.01001001001001001, 2.1121121121121122), distance du centre = 0.9963178739552048
Calcul terminé et workers libérés.