11
votes

Pourquoi mon multi-threading n'est-il pas efficace?

J'ai conçu une classe qui remplit un tableau avec des entiers utilisant un nombre différent de threads, afin de voir la puissance de la filtration multi-threading. Mais selon mon résultat, il n'y a aucun ...

l'idée forte>: l'idée était trop remplir un tableau de 100000000 entiers avec la valeur "1". En commençant par 1 thread (un threads remplit l'ensemble de la matrice) et l'incrémentation jusqu'à 100 threads (chaque thread remplit une sous-réseau de taille 100000000 / NBThreads) p>

strong>: avec 10 threads , Je crée 10 threads et chacun remplit une matrice d'entiers 10000000. P>

Voici mon code: p>

1 THREADS: 196ms
2 THREADS: 208ms
3 THREADS: 222ms
4 THREADS: 213ms
5 THREADS: 198ms
6 THREADS: 198ms
7 THREADS: 198ms
8 THREADS: 198ms
9 THREADS: 198ms
10 THREADS: 206ms
11 THREADS: 201ms
12 THREADS: 197ms
13 THREADS: 198ms
14 THREADS: 204ms
15 THREADS: 199ms
16 THREADS: 203ms
17 THREADS: 234ms
18 THREADS: 225ms
19 THREADS: 235ms
20 THREADS: 235ms
21 THREADS: 234ms
22 THREADS: 221ms
23 THREADS: 211ms
24 THREADS: 203ms
25 THREADS: 206ms
26 THREADS: 200ms
27 THREADS: 202ms
28 THREADS: 204ms
29 THREADS: 202ms
30 THREADS: 200ms
31 THREADS: 206ms
32 THREADS: 200ms
33 THREADS: 205ms
34 THREADS: 203ms
35 THREADS: 200ms
36 THREADS: 206ms
37 THREADS: 200ms
38 THREADS: 204ms
39 THREADS: 205ms
40 THREADS: 201ms
41 THREADS: 206ms
42 THREADS: 200ms
43 THREADS: 204ms
44 THREADS: 204ms
45 THREADS: 206ms
46 THREADS: 203ms
47 THREADS: 204ms
48 THREADS: 204ms
49 THREADS: 201ms
50 THREADS: 205ms
51 THREADS: 204ms
52 THREADS: 207ms
53 THREADS: 202ms
54 THREADS: 207ms
55 THREADS: 207ms
56 THREADS: 203ms
57 THREADS: 203ms
58 THREADS: 201ms
59 THREADS: 206ms
60 THREADS: 206ms
61 THREADS: 204ms
62 THREADS: 201ms
63 THREADS: 206ms
64 THREADS: 202ms
65 THREADS: 206ms
66 THREADS: 205ms
67 THREADS: 207ms
68 THREADS: 210ms
69 THREADS: 207ms
70 THREADS: 203ms
71 THREADS: 207ms
72 THREADS: 205ms
73 THREADS: 203ms
74 THREADS: 211ms
75 THREADS: 202ms
76 THREADS: 207ms
77 THREADS: 204ms
78 THREADS: 212ms
79 THREADS: 203ms
80 THREADS: 210ms
81 THREADS: 206ms
82 THREADS: 205ms
83 THREADS: 203ms
84 THREADS: 203ms
85 THREADS: 209ms
86 THREADS: 204ms
87 THREADS: 206ms
88 THREADS: 208ms
89 THREADS: 263ms
90 THREADS: 216ms
91 THREADS: 230ms
92 THREADS: 216ms
93 THREADS: 230ms
94 THREADS: 234ms
95 THREADS: 234ms
96 THREADS: 217ms
97 THREADS: 229ms
98 THREADS: 228ms
99 THREADS: 215ms
100 THREADS: 232ms


9 commentaires

@nbartaille, combien de cœurs avez-vous sur votre machine?


"Exemple: avec 10 threads, je crée 10 threads et chacun remplit une matrice d'entiers 10000000." - Je suppose que vous voulez dire que chaque fil remplit un 1/10 de la matrice?


Dsolimano: 2Cores sur cette machine


@nbartaille, j'ai testé votre code sur ma machine (2 cœurs) après avoir corrigé l'inadéquation évidente entre les noms de classe et de constructeur, et j'ai une augmentation assez significative des performances pour 2 threads: 800 ms pour 1 fil, 500 ms pour 2 threads. Augmentation ultérieure n'a pas beaucoup changé.


Sergey: Quelle quantité de mémoire avez-vous sur votre ordinateur?


@nbartaille, 2 Go. Et il a un atome d'extel lent N450, c'est peut-être pour la raison pour laquelle votre code s'est avéré être assez intensif de la CPU pour mon netbook. (Remarque: Utilisez @Name NOTION lors de votre réponse à une personne, de cette façon les gens sont informés de vos réponses. Seul le premier @name dans le commentaire, cependant.)


@nbartaille lorsque vous faites de telles mesures, vous devez toujours autoriser une phase d'échauffement auparavant. Le JRE doit probablement faire un chargement de classe, la compilation et similaires pendant que vous mesurez déjà la première course. Je viens de l'essayer sur ma machine en emballant tout dans une boucle. Là, j'ai pu atteindre une vitesse de 10% en utilisant 1 fil.


Êtes-vous sûr que vous cognez 2 cœurs, et pas seulement 1 noyau avec hyperthreading activé?


Ce site a-t-il mal qui a mal "Comment écrire un micro-benchamark?" et "ce que les diables sont des cache-miss?" guides.


4 Réponses :


9
votes

Multithraleading est super efficace lorsque votre logiciel est lié à la CPU: il y a beaucoup d'applications qui sont mono-filetées et vous pouvez les voir sous-aliments douloureusement sous les processeurs modernes par maxxing, une utilisation d'un noyau (ceci apparaît très clairement dans les moniteurs de la CPU) .

Cependant, il ne sert à rien de lancer de nombreux autres threads que le nombre de processeurs (virtuels) disponibles.

Les applications multi-threadées correctement multiples qui font, par exemple, le nombre de chiffres numériques, créent un certain nombre de threads de travailleurs liés au nombre de processeurs (virtuels) disponibles pour la JVM.


7 commentaires

Oui, c'est ce que j'attendais: comme mon ordinateur dispose de 2 processeurs, je m'attendais à voir une réelle augmentation des performances entre 1 et 2 cœurs, puis un ralentissement d'un grand nombre de fils. Mais aucune de mes hypothèse n'a été vérifiée ...


@nbartaille - Votre code n'est probablement pas lié à la CPU, mais plus limité par la vitesse d'accès à la mémoire.


Justin: Puis-je modifier ceci en modifiant la quantité de mémoire allouée au JVM? Si oui, comment puis-je le faire sur Eclipse?


@nbartaille: Je pense que vous devez trouver quelque chose d'autre pour que vos discussions soient plus intenses de la CPU ... L'augmentation de la mémoire allouée ne changera rien.


Le code n'a pas besoin de la CPU lié au bénéfice de multithreading. Essayez d'accéder à une base de données par exemple. 1 Le fil effectuant 100 requêtes sera beaucoup plus lent que 10 threads effectuant 10 requêtes chacune. Certains threads attendent, dormir, bloqués bloqués ou tout ce que vous le nommez, d'autres peuvent continuer à fonctionner. Dans de tels scénarios, il est certainement logique d'utiliser (même beaucoup) plus de threads que les processeurs disponibles. Si cela ne serait pas le cas, la multithreading n'aurait jamais de sens sur des processeurs de base uniques que cela le fait certainement.


Je suis vraiment d'accord avec la chose limitée de la mémoire. Lorsque je l'exécute sur des tableaux plus petits, je vois une augmentation de la version 2Headed.


@sfusseengger: +1 à votre commentaire: Notez que je n'ai pas dit que ce n'était pas efficace dans d'autres cas. Je viens de dire que la multi-threading était super efficace sur les applications liées à la CPU. Et je sais que tu n'as pas dit que j'avais tort;)



4
votes

La tâche que vous effectuez à l'intérieur du thread est si minuscule, le temps utilisé pour celui-ci est dérégé par la surcharge de votre configuration.

Faites un certain calcul lourd (par exemple, une approximation de PI pour mettre dans la matrice), vous verrez un avantage de plusieurs threads, mais seulement jusqu'à approximativement le nombre de noyaux de votre machine.

ou faire quelque chose qui attend quelque chose d'externe (lecture d'une base de données, gratter des données d'un site Web) Cela pourrait être plus performant tant que d'autres threads font quelque chose d'utilité tandis que d'autres attendent.


0 commentaires

20
votes

Avec deux noyaux, les meilleures performances que vous pouvez vous attendre à ce que 2 fils prennent la moitié du temps comme un fil. Tous les fils supplémentaires ne créent que des frais généraux inutiles après cela - en supposant que vous êtes complètement liés à la CPU, mais que vous n'êtes pas réellement.

La question est de savoir pourquoi vous ne voyez pas une amélioration lorsque vous passez de 1 à 2 threads. Et la raison est probablement que votre programme n'est pas lié à la CPU, mais à la mémoire de la mémoire. Votre goulot d'étranglement est l'accès principal de la mémoire et les 2 threads ne font que tourner à tour de rôle dans la mémoire principale. Les cœurs de la CPU ne font rien que la plupart du temps. Vous verrez la différence attendue si au lieu de faire peu de travail réel sur une grande zone de mémoire, vous faites beaucoup de travail intensif de la CPU sur une petite quantité de mémoire. Parce que chaque noyau de la CPU peut fonctionner à l'intérieur de son cache.


5 commentaires

Merci. J'ai eu le point pourquoi je ne vois aucune augmentation de performance entre 1 et 2 threads. Mais comment pouvez-vous expliquer que les performances ne sont pas dégradantes pour 100 threads?


Question supplémentaire: On m'a donné pour comprendre que dans certaines applications, il pourrait être plus efficace d'utiliser beaucoup plus de threads que votre nombre de processeur, lorsque le facteur de limitation n'est pas un processeur ou une mémoire (comme disque ou accès au réseau). Es-tu d'accord avec ça?


@nbartaille: Oui, c'est un peu vrai. L'idée est que certains threads peuvent utiliser la CPU tandis que d'autres attendent l'IO. Cependant, il est préférable d'utiliser des threads séparés (ou des piscines de fils) pour ces tâches. Vous ne voulez généralement pas avoir plus d'un fil accédant au disque et qu'un thread peut remettre des tâches sur un pool de fils de calcul. Quant à ne pas voir la dégradation avec de nombreux threads, je ne suis pas sûr. Peut-être que chaque fil devient essentiellement faire sa part en séquence, il n'ya donc pas de surcharge sur du haut de contention et la création de threads est cachée en ayant deux noyaux qui alternent ou sont simplement petits.


Si vous essayez de profiler votre application, dites-le avec deux threads en cours d'exécution, vous devriez pouvoir voir quelles instructions se dirigent vers la tête en perçant au niveau du montage. Cela devrait également vous donner un indice sur où expérimenter des séquences de code alternatives pour améliorer le parallellisme. Toute autre manière est vraiment une perte de temps jusqu'à ce que vous ayez acquis une expérience de telle sorte que vous avez déjà eu lieu pendant que les points chauds se produiront.


"Vous ne voulez généralement pas avoir plus d'un fil accédant au disque" Cela peut sembler une bonne idée, mais si vous utilisez WrichFile et Readfile (qui sont le fil-coffre-fort) avec IOCPS sous Windows, il appartient au disque. Sous-système d'E / S pour planifier quand (en ce qui concerne les autres accès en attente) et comment (peut-être combiné avec d'autres accès. Avoir un fil d'accès à un accès disque sera dans de tels cas être une utilisation très inefficace des ressources.



0
votes

Il est possible pour deux threads - chacun avec son propre processeur ou son noyau - travaillant à l'unisson, pour compléter une tâche plus lente que si un seul fil a fait tout le travail. Les deux noyaux veulent que leurs caches L1 + L2 doivent écrire des données à la mémoire qui va bien. Cependant, ils sottent bientôt le cache L3 commun de telle sorte qu'il arrête d'écrire des écrires supplémentaires jusqu'à ce qu'il ait réussi à écrire une ligne de cache mise à jour à la RAM, libérant ainsi d'accepter de nouvelles écrivies.

Pour mettre un autre moyen, le but de vos threads n'est pas d'effectuer un traitement à la parole, mais de remplir le système RAM. La RAM système est lente et comme vous pouvez le constater en comparant votre résultat à un thread à celui de deux threads, la capacité en écriture à la carte RAM est utilisée avec un seul fil et ne peut donc pas être plus rapide avec deux threads.

Vos fils sont si petits que, dans toutes les probabilités, ils résident dans le cache L1 et ne nécessitent donc pas de récupération de la RAM du système qui entraverait votre capacité à faire des écrires de RAM. Votre capacité à écrire à la RAM est la même que vous avez 1 ou 100 threads essayant de le faire. Plus vous aurez des threads que vous avez, plus vous aurez sur le dessus de l'administration de fil. Ceci est négligeable pour peu de threads mais augmente pour chaque fil supplémentaire et deviendra éventuellement perceptible.


0 commentaires