Deux threads sur un CPU à huit cœurs — et pourtant, aucune accélération. Le calcul reste sérialisé.Two threads on an eight-core CPU — and yet no speedup at all. The computation stays serialized.
Le générateur suspendu du n°21 mettait sa propre exécution en pause, à un point précis, pour rendre la main. Un thread, lui, semble tourner en même temps que les autres — c'est même la promesse du multithreading : répartir le travail sur plusieurs cœurs pour aller plus vite. Cette intuition est fausse en Python, et ce numéro explique pourquoi : le Global Interpreter Lock, le GIL, garantit qu'un seul thread exécute du bytecode Python à un instant donné, quel que soit le nombre de cœurs disponibles. Diviser un calcul en deux threads ne le fait donc pas tourner deux fois plus vite — au mieux, ça ne change rien ; au pire, la coordination entre threads ralentit l'ensemble.Issue №21's suspended generator paused its own execution at a precise point to hand back control. A thread, by contrast, looks like it runs at the same time as the others — that's even the promise of multithreading: spread the work across multiple cores to go faster. That intuition is wrong in Python, and this issue explains why: the Global Interpreter Lock, the GIL, guarantees that only one thread executes Python bytecode at any given instant, no matter how many cores are available. Splitting a computation across two threads therefore doesn't run it twice as fast — at best, nothing changes; at worst, the coordination between threads slows the whole thing down.
import threading, time def compter(n): while n > 0: n -= 1 debut = time.perf_counter() compter(50_000_000) print("séquentiel :", time.perf_counter() - debut) debut = time.perf_counter() t1 = threading.Thread(target=compter, args=(25_000_000,)) t2 = threading.Thread(target=compter, args=(25_000_000,)) t1.start(); t2.start() t1.join(); t2.join() print("deux threads :", time.perf_counter() - debut) # sur un CPU à 8 cœurs, on attend une division par ~2 -- il n'y en a # quasiment aucune. Parfois, c'est même PLUS LENT que la version # séquentielle.
Avant de lancer un threading.Thread, demande : ce travail attend-il quelque chose (I/O), ou calcule-t-il (CPU) ? Le GIL ne pénalise que le second cas.Before spinning up a threading.Thread, ask: is this work waiting on something (I/O), or computing (CPU)? The GIL only penalizes the latter.