La regola empirica per i thread è che si desidera almeno un thread di lavoro "attivo" (in grado di eseguire immediatamente i comandi alla volta della CPU) per ogni "unità di esecuzione" disponibile sul computer. Una "unità di esecuzione" è un processore di istruzioni logico, quindi un server hyperthreaded Xeon quad-chip e quad-core avrebbe 32 EU (4 chip, 4 core per chip, ognuno hyperthreaded). Il tuo Core i7 medio avrebbe 8.
Un thread per UE è il massimo utilizzo della potenza della CPU, a condizione che i thread siano sempre in esecuzione; questo non è quasi mai il caso, poiché i thread hanno bisogno dell'accesso alla memoria non cache, al disco rigido, alle porte di rete, ecc. che devono attendere e che non richiedono l'attenzione della CPU attiva per essere eseguiti. È quindi possibile aumentare ulteriormente l'efficienza complessiva con più thread in coda e raramente disponibili. Questo ha un costo; quando una CPU scambia un thread, deve memorizzare nella cache i registri del thread, il puntatore di esecuzione e altre informazioni sullo stato normalmente mantenute nei meccanismi più interni di un UE e molto rapidamente accessibili, consentendo ad altri UE in quel chip CPU di raccoglierlo. Richiede inoltre thread nel sistema operativo per decidere a quale thread deve essere attivato. Infine, quando un'UE cambia thread, perde i guadagni in termini di prestazioni della pipeline utilizzata dalla maggior parte delle architetture di processori; deve svuotare la tubazione prima di cambiare thread. Ma, poiché tutto ciò richiede ancora molto meno tempo in media rispetto alla semplice attesa del ritorno del disco rigido o della RAM con le informazioni, ne vale la pena.
Tuttavia, in generale, una volta superato il doppio del numero di thread "attivi" rispetto agli europei, il sistema operativo inizia a dedicare più tempo ai thread di pianificazione del tempo dell'UE e gli UE trascorrono più tempo a passare da uno all'altro, rispetto a quelli effettivamente utilizzati per eseguire thread attivi di programmi. Questo è il punto di diseconomie di scala; in realtà ci vorrà più tempo per l'esecuzione di un algoritmo multithread se si dovesse aggiungere un thread aggiuntivo a questo punto.
Quindi, nel complesso, vuoi mantenere nel tuo programma almeno tanti thread quanti sono gli EU sul computer, ma vuoi evitare di avere più del doppio di quel numero che non sta aspettando o dormendo.