Quelle est la différence entre-
newSingleThreadExecutor vs newFixedThreadPool(20)
en termes de système d'exploitation et de programmation.
Chaque fois que j'exécute mon programme en utilisant newSingleThreadExecutor
mon programme fonctionne très bien et la latence de bout en bout (95e centile) arrive autour de 5ms
.
Mais dès que je commence à exécuter mon programme en utilisant-
newFixedThreadPool(20)
les performances de mon programme se dégradent et je commence à voir la latence de bout en bout comme 37ms
.
Alors maintenant, j'essaie de comprendre du point de vue de l'architecture que signifie le nombre de threads ici? Et comment décider quel est le nombre optimal de threads que je devrais choisir?
Et si j'utilise plus de threads, que se passera-t-il?
Si quelqu'un peut m'expliquer ces choses simples dans une langue profane, cela me sera très utile. Merci pour l'aide.
Ma spécification de configuration de la machine - J'exécute mon programme à partir d'une machine Linux -
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 45
model name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
stepping : 7
cpu MHz : 2599.999
cache size : 20480 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc Arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts
bogomips : 5199.99
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 45
model name : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
stepping : 7
cpu MHz : 2599.999
cache size : 20480 KB
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc Arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts
bogomips : 5199.99
clflush size : 64
cache_alignment : 64
address sizes : 40 bits physical, 48 bits virtual
power management:
D'accord. Idéalement, en supposant que vos threads n'ont pas de verrouillage de sorte qu'ils ne se bloquent pas (indépendamment les uns des autres) et que vous pouvez supposer que la charge de travail (traitement) est la même, il s'avère que, avoir une taille de pool de fonction Runtime.getRuntime().availableProcessors()
ou availableProcessors() + 1
donne les meilleurs résultats.
Mais disons que si les threads interfèrent les uns avec les autres ou ont des E/S impliquées, alors la loi d'Amadhal explique assez bien. De wiki,
La loi d'Amdahl stipule que si P est la proportion d'un programme qui peut être rendu parallèle (c'est-à-dire bénéficier de la parallélisation), et (1 - P) est la proportion qui ne peut pas être parallélisée (reste sérielle), alors l'accélération maximale qui peut être atteint en utilisant N processeurs est
Dans votre cas, en fonction du nombre de cœurs disponibles et de leur travail précis (calcul pur? E/S? Verrouillage des verrous? Bloqué pour certaines ressources? Etc.), vous devez trouver la solution basée sur ce qui précède paramètres.
Par exemple: Il y a quelques mois, j'ai participé à la collecte de données à partir de sites Web numériques. Ma machine était à 4 cœurs et j'avais un pool de 4
. Mais parce que l'opération était purement I/O
et ma vitesse nette était décente, j'ai réalisé que j'avais les meilleures performances avec une taille de piscine de 7
. Et c'est parce que les threads ne se battaient pas pour la puissance de calcul, mais pour les E/S. Je pourrais donc tirer parti du fait que davantage de threads peuvent contester le noyau de manière positive.
PS: Je suggère, en parcourant le chapitre Performances du livre - Java Concurrency in Practice par Brian Goetz. Il traite de ces questions en détail.
Alors maintenant, j'essaie de comprendre du point de vue de l'architecture que signifie le nombre de threads ici?
Chaque thread a sa propre mémoire de pile, un compteur de programme (comme un pointeur sur l'instruction qui s'exécute ensuite) et d'autres ressources locales. Les échanger nuit à la latence pour une seule tâche. L'avantage est que même si un thread est inactif (généralement en attendant les E/S), un autre thread peut faire son travail. De même, si plusieurs processeurs sont disponibles, ils peuvent s'exécuter en parallèle s'il n'y a pas de conflit de ressources et/ou de verrouillage entre les tâches.
Et comment décider quel est le nombre optimal de threads que je devrais choisir?
Le compromis entre le prix d'échange par rapport à la possibilité d'éviter le temps d'inactivité dépend des petits détails de l'apparence de votre tâche (combien d'E/S et quand, avec combien de travail entre les E/S, en utilisant la quantité de mémoire pour Achevée). L'expérimentation est toujours la clé.
Et si j'utilise plus de threads, que se passera-t-il?
Il y aura généralement une croissance linéaire du débit au début, puis une partie relativement plate, puis une baisse (qui peut être assez abrupte). Chaque système est différent.
Regarder la loi d'Amdahl est très bien, surtout si vous savez exactement quelle est la taille de P et N. Étant donné que cela ne se produira jamais vraiment, vous pouvez surveiller les performances (ce que vous devriez faire de toute façon) et augmenter/diminuer la taille de votre pool de threads pour optimiser les mesures de performances qui sont importantes pour vous.