Bienvenue ! Quel est votre objectif aujourd'hui ?

Sélectionnez votre profil pour adapter l'arborescence et accéder directement aux contenus pertinents.

Freelance & Tech

Je suis un recruteur / Tech Lead

Vous cherchez un développeur freelance Senior Go / Fullstack, un renfort d'équipe ou une expertise d'architecture.

Voir le profil Recruteur →
Studio & Logiciel

Je souhaite construire mon logiciel

Vous avez un besoin métier sur-mesure, une application web/mobile à concevoir ou un MVP à concrétiser.

Voir le profil Sur-Mesure →
Courses/Performance For Backend/Slots/J3_am

3. Coût Mémoire & Bascule de Contexte

Comparatif matériel entre un thread noyau et une goroutine Go :

1. Taille de la Pile

Thread OS : 1 à 2 Mo fixe réservés d'office.
Goroutine : ~2 Ko extensible dynamiquement.

2. Création / Destruction

Thread OS : appel système lourd (syscall).
Goroutine : allocation utilisateur en quelques nanosecondes.

3. Bascule de Contexte

Thread OS : 1000 à 2000 cycles CPU (kernel).
Goroutine : 10 à 20 cycles CPU (user-space).


4. L'Ordonnanceur Go & Work Stealing

L'ordonnanceur Go (modèle GMP) repose sur un triplet architectural et un équilibrage dynamique de charge :

G Goroutine (~2 Ko)

Pile d'exécution extensible, pointeur d'instruction et code léger.

Unité logique.
M Machine (Thread OS)

Thread noyau du système d'exploitation exécuté sur un cœur physique.

Thread matériel OS.
P Processeur Logique

Contexte d'exécution et file locale (défini par GOMAXPROCS).

File locale sans verrou.
  • Vol de Travail (Work Stealing) : Lorsqu'un P vide sa file, il vole la moitié des goroutines d'un autre P sans goulet d'étranglement central.
  • Délestage I/O Bloquantes : Si une goroutine subit un syscall bloquant, M libère P pour qu'il continue d'exécuter d'autres tâches.

5. Verrous d'Exclusion Mutuelle

L'accès à une variable partagée en écriture impose de garantir l'exclusion mutuelle :

 1type SafeCounter struct {
 2    mu    sync.Mutex
 3    value int
 4}
 5
 6func (c *SafeCounter) Increment() {
 7    c.mu.Lock()
 8    c.value++ // Section critique protégée
 9    c.mu.Unlock()
10}
  • Règle fondamentale : Une seule goroutine à la fois peut pénétrer dans la section critique.
  • Question clé : Que se passe-t-il quand des dizaines de cœurs demandent le même verrou simultanément ?

6. Le Phénomène de Contention

Quand plusieurs cœurs se disputent le même verrou, les performances s'effondrent :

Contention de Verrou

1. Les goroutines en attente sont suspendues (Parked) par l'ordonnanceur.
2. Le réveil des threads engendre des bascules de contexte et des invalidations de cacheline MESI en boucle.
3. Le coût de gestion du verrou devient supérieur au temps de calcul effectif.


7. L'Écroulement de la Loi d'Amdahl

Ajouter des cœurs CPU ne résout pas la contention : cela aggrave le blocage matériel :

  • Loi d'Amdahl : L'accélération maximale d'un système est bornée par sa fraction strictement séquentielle.
  • Loi d'Universal Scalability (USL) : Sous contention, l'effort de coordination inter-cœurs fait chuter le débit net.
  • Conséquence directe : Au-delà d'un seuil critique, passer de 8 à 32 cœurs divise le débit au lieu de le multiplier !

8. Primitives Atomiques & Instructions LOCK

Les opérations atomiques manipulent la mémoire directement via le contrôleur matériel sans passer par le noyau :

1import "sync/atomic"
2
3type AtomicCounter struct {
4    value int64
5}
6
7func (c *AtomicCounter) Increment() {
8    atomic.AddInt64(&c.value, 1) // Compile en LOCK XADDQ au niveau silicium
9}
  • Zéro appel système, zéro mise en veille : L'instruction processeur (LOCK CMPXCHG / LOCK XADDQ) verrouille temporairement la ligne de cache pour exécuter la modification en 1 cycle.

9. Modèle Read-Copy-Update (RCU)

Pour les structures très souvent lues et rarement écrites (tables de routage, configurations) :

1 Lectures Sans Verrou

Les lecteurs accèdent directement aux données via un pointeur atomique en 0 ns sans contention.

Scalabilité infinie en lecture.
2 Copie en Écriture

L'écrivain duplique la structure, applique ses modifications sur la copie privée isolée.

Aucun blocage des lecteurs.
3 Permutation Atomique

L'écrivain intervertit le pointeur avec StorePointer. Les nouveaux lecteurs voient la nouvelle version.

Transition instantanée.

10. Implémentation du Pattern RCU

Exemple canonique d'une table de routage avec lecture ultra-rapide et swap atomique sans blocage :

 1type RoutingTable struct {
 2    routes atomic.Pointer[map[string]Handler]
 3}
 4
 5// Lecture ultra-rapide sans verrou (1 cycle)
 6func (rt *RoutingTable) Get(path string) Handler {
 7    m := rt.routes.Load()
 8    if m == nil { return nil }
 9    return (*m)[path]
10}
11
12// Mise à jour sur copie privée puis permutation atomique
13func (rt *RoutingTable) Set(path string, h Handler) {
14    oldMap := rt.routes.Load()
15    newMap := make(map[string]Handler)
16    if oldMap != nil {
17        for k, v := range *oldMap { newMap[k] = v }
18    }
19    newMap[path] = h
20    rt.routes.Store(&newMap) // Swap atomique instantané
21}

11. Synthèse Concurrence & Verrous

Les 3 règles d'or pour la concurrence backend :

1 Privilégier l'Atome

Remplacer les verrous par sync/atomic pour les compteurs et drapeaux simples.

Instructions matérielles.
2 Adopter le RCU

Utiliser atomic.Pointer pour les données lues massivement et modifiées rarement.

Lectures sans verrou.
3 Réduire les Sections Critiques

Garder les sections protégées par Mutex aussi courtes que possible pour éviter la contention.

Débit préservé.

TP Fil Rouge (Séance 5) : Parallélisation & Worker Pool Borné

Activité Pratique • 3h30 • Objectif Multi-Cœurs

Mission : Découper l'espace de recherche de la cible @kAl1, distribuer les lots sur un Worker Pool borné au nombre de cœurs CPU et propager l'annulation précoce instantanée.

1. Partitionnement Combinatoire

Découper l'espace de recherche (ex. partitionner par le premier caractère de @kAl1) et distribuer les blocs de candidats via un canal Go (channel).

2. Pool Borné sur runtime.NumCPU()

Construire un Worker Pool borné exactement sur le nombre de cœurs logiques de la machine pour éviter les pénalités d'ordonnancement liées au surplus de goroutines.

3. Arrêt Précoce context.WithCancel

Mettre en place l'arrêt précoce non bloquant : dès qu'un worker découvre le mot de passe, propager l'annulation instantanée à tous les workers via un context.WithCancel pour libérer immédiatement le CPU.

4. Scaling Multi-Cœurs

Mesurer l'accélération (Speedup) en fonction du nombre de cœurs alloués (1, 2, 4, N cœurs) et vérifier la linéarité du passage à l'échelle.