A regra geral para threads é que você deseja pelo menos um thread de trabalho "ativo" (capaz de executar seus comandos imediatamente após o tempo da CPU) para cada "unidade de execução" disponível no computador. Uma "unidade de execução" é um processador de instruções lógicas, portanto, um servidor Xeon com quatro núcleos e quatro núcleos com hyperthread teria 32 EUs (4 chips, 4 núcleos por chip, cada hyperthread). Seu Core i7 médio teria 8.
Um encadeamento por UE é o uso máximo da energia da CPU, desde que os encadeamentos estejam sempre em estado de execução; quase nunca é esse o caso, pois os threads precisam acessar a memória não armazenada em cache, o disco rígido, as portas de rede etc. que eles devem esperar e que não exigem atenção ativa da CPU para serem executados. Dessa forma, você pode aumentar ainda mais a eficiência geral com mais threads na fila e ansiosos para começar. Isso tem um custo; quando uma CPU alterna um encadeamento, deve armazenar em cache os registros, o ponteiro de execução e outras informações de estado normalmente mantidas no funcionamento mais interno de uma UE e acessadas muito rapidamente, permitindo que outras UEs nesse chip da CPU possam buscá-lo. Também requer threads no sistema operacional para decidir para qual thread deve ser alternado. Por fim, quando uma UE alterna threads, perde os ganhos de desempenho do pipelining usado pela maioria das arquiteturas de processador; ele precisa liberar o pipeline antes de alternar os threads. Mas, como tudo isso ainda leva muito menos tempo, em média, do que simplesmente esperar o disco rígido ou até a RAM voltar com informações, vale a pena o custo.
No entanto, em geral, quando você ultrapassa o dobro do número de threads "ativos" que os EUs, o sistema operacional começa a gastar mais dos threads de agendamento de tempo dos EUs e os EUs passam mais tempo alternando entre eles do que realmente são gastos executando threads ativos de programas. Este é o ponto das deseconomias de escala; na verdade, levará mais tempo para que um algoritmo multithread seja executado se você adicionar um thread extra nesse momento.
Portanto, no geral, você deseja manter pelo menos o número de threads em seu programa que possui UEs no computador, mas deseja evitar ter mais que o dobro do número que não está esperando ou dormindo.