7
votes

Que éviter pour des raisons de performance dans le code multithreaded?

Je suis en train de passer en revue / refactorise une application multithreadée qui est supposée être multithreadée afin de pouvoir utiliser tous les cœurs disponibles et livrer théoriquement une performance meilleure / supérieure (Supérieure est le terme commercial pour le meilleur: p) < / p>

Quelles sont les choses que je devrais être au courant lors de la programmation des applications multithreadées?

Je veux dire des choses qui auraient considérablement une incidence sur la performance, peut-être même au point où vous ne gagnez rien avec du multithreading mais perdez beaucoup par la complexité de conception. Quels sont les gros drapeaux rouges pour des applications multithreading?

Devrais-je commencer à remettre en question les serrures et à la recherche d'une stratégie sans verrouillage ou existe-t-il d'autres points plus importants qui devraient allumer un éclairage d'avertissement?

edit: Le genre de réponses que j'aimerais ressembler à la réponse de Janusz, je veux des avertissements rouges pour rechercher le code, je sais que l'application ne fonctionne pas aussi bien que celle-ci Devrait-je avoir besoin de savoir où commencer à chercher, qu'est-ce qui devrait m'inquiéter et où devrais-je mettre mes efforts. Je sais que c'est une sorte de question générale, mais je ne peux pas poster l'ensemble du programme et si je pouvais choisir une section du code, je ne devrais pas avoir besoin de demander en premier lieu.

J'utilise DELPHI 7, bien que l'application soit portée / refaire dans .NET (C #) pour l'année suivante, je préfère donc entendre des commentaires applicables comme une pratique générale et si elles doivent être spécifiques à soit une de ces langues


0 commentaires

12 Réponses :


0
votes

Vous devez d'abord obtenir un outil pour surveiller les threads spécifiques à votre langue, à votre cadre et à votre IDE. Votre propre enregistreur pourrait bien faire bien (le temps de reprise, le temps de sommeil + durée). De là, vous pouvez vérifier les mauvais traitements qui n'exécutent pas beaucoup ou attendent trop longtemps pour que quelque chose se produise, vous voudrez peut-être faire l'événement qu'ils attendent le plus tôt possible.

Comme vous voulez utiliser les deux noyaux, vous devriez vérifier l'utilisation des noyaux avec un outil qui peut grapher l'utilisation du processeur sur les deux noyaux pour votre application uniquement ou assurez-vous simplement que votre ordinateur est aussi inactif que possible.

En outre que vous devriez présenter votre application pour vous assurer que les choses effectuées dans les threads sont efficaces, mais faites attention à une optimisation prématurée. Aucun sens d'optimiser votre multiprofessionnement si les threads eux-mêmes fonctionnent mal.

Vous recherchez une stratégie sans verrouille peut vous aider beaucoup, mais il n'est pas toujours possible d'obtenir votre application pour effectuer de manière sans verrouillage.


0 commentaires

4
votes

Plus de threads alors il y a des noyaux, signifie généralement que le programme ne fonctionne pas de manière optimale.

Ainsi, un programme qui apparaît généralement des charges de threads n'est généralement pas conçu de la meilleure mode. Un bon exemple de cette pratique sont les exemples de prise classique où chaque connexion entrante obtient son propre thread de manipuler la connexion. C'est une façon très non évolutive de faire des choses. Plus il y a de threads, plus le système d'exploitation devra utiliser le système d'exploitation pour changer de contexte entre les threads.


2 commentaires

ERR, comme d'habitude, cela dépend. Dans certaines circonstances (lorsque vous vous attendez à ce que chaque fil ait à passer de longues périodes en attente d'événements), cela ne peut avoir aucun impact sur la performance et rendre le code beaucoup plus simple.


Dans ce cas, il est beaucoup plus agréable d'utiliser une architecture asynchrone et ne s'appuie pas sur des fils du tout.



1
votes

Les profileurs d'exécution peuvent ne pas bien fonctionner avec une application multi-threadée. Néanmoins, tout ce qui rend une application à une seule application lente rendra également une application multi-threadée lente. Il peut être une idée d'exécuter votre application en tant qu'application à une seule-filetage et d'utiliser un profileur, pour savoir où se trouvent ses points d'accès à la performance (goulots d'étranglement).

Lorsqu'il est en cours d'exécution en tant qu'aplication multi-threadé, vous pouvez utiliser l'outil de surveillance des performances du système pour voir si les verrous posent un problème. En supposant que vos discussions seraient verrouillées au lieu d'attendre occupé, alors avoir 100% de CPU pour plusieurs threads est un signe que le verrouillage n'est pas un problème. Inversement, quelque chose qui ressemble à une utilisation totale du processeur de 50% sur une machine à double processeur est un signe qu'un seul thread est en cours d'exécution et que votre verrouillage est peut-être un problème qui empêche plus d'un fil simultané (lors de la comptage du nombre de CPU. votre machine, méfiez-vous multicœur et hyperthreading).

Les serrures ne sont pas uniquement dans votre code, mais également dans les API que vous utilisez: E.G. Le gestionnaire de démarrage (chaque fois que vous allouez et supprimez la mémoire), peut-être dans votre implémentation de l'enregistreur, peut-être dans certaines des API O / S, etc.

Devrais-je commencer à remettre en question les serrures et à la recherche d'une stratégie sans verrouillage

Je remets toujours en question les serrures, mais je n'ai jamais utilisé une stratégie sans verrouillage; Au lieu de cela, mon ambition est d'utiliser des serrures si nécessaire, de sorte que c'est toujours threadsafe mais que jamais l'impasse et pour que les verrous soient acquis pendant une minute de temps (par exemple, pas plus de temps que le temps nécessaire à la poussée ou à la pop Pointeur sur une file d'attente à fil), de sorte que la quantité maximale de temps qu'un thread peut être bloqué est insignifiant par rapport au temps qu'il dépense faire du travail utile.


0 commentaires

5
votes

Une chose qui diminue la performance consiste à avoir deux threads avec beaucoup d'accès au disque dur. Le disque dur sauterait de fournir des données pour un fil à l'autre et les deux threads attendraient le disque tout le disque.


1 commentaires

C'est un compromis. Si le processus utilise beaucoup de CPU par rapport au disque, il peut s'agir d'une victoire. Il s'agit généralement d'une victoire dans la génération de vignettes multimédia. Cependant, il est pas une victoire lorsque la source de données est un CD-ROM. :)



6
votes

Une chose à éviter définitivement est beaucoup d'accès en écriture aux mêmes lignes de cache des threads.

Par exemple: Si vous utilisez une variable de compteur pour compter le nombre d'éléments traités par tous les threads, cela vous fera mal de la performance car les lignes de cache de la CPU doivent se synchroniser chaque fois que l'autre CPU écrit à la variable.


3 commentaires

Agréable! Je ne savais pas que j'ai une section où je fais une incrémentation verrouillée (en fait: une "Lock Inc" dans Assembleur) et je n'ai jamais arrêté de penser à des lignes de cache. +1, je peux donner un +2


@Zan Lynx est-ce vrai même si vous avez utilisé une variable atomique? +1 sur le nom de Jorge. :)


@Kazark: Oui. L'accès atomique (avec verrouillage sur Intel) est encore plus lent dans du matériel.



2
votes

Qu'est-ce qui tue les performances lorsque deux ou plusieurs threads partagent les mêmes ressources. Cela pourrait être un objet qui utilise ou un fichier qui utilise à la fois un réseau à la fois ou un processeur qui utilise à la fois. Vous ne pouvez pas éviter ces dépendances sur les ressources partagées, mais si possible, essayez d'éviter de partager des ressources.


0 commentaires

5
votes

Quelque chose à garder à l'esprit lors du verrouillage: verrouiller aussi peu de temps que possible. Par exemple, au lieu de cela: xxx

fais cela (si possible): xxx

bien sûr, cet exemple ne fonctionne que si DosomiquandingIftrue () et DosomobliSiffalse () Ne requérez pas de synchronisation, mais il illustre ce point: le verrouillage aussi bref le plus possible possible, tout en améliorant toujours votre performance. La sécurité de votre code en ce sens qu'elle réduit la surface des problèmes de synchronisation.

et dans certains cas, cela améliorera les performances. Rester enfermé pendant de longues longueurs signifie que d'autres threads en attente d'accès à certaines ressources vont attendre plus longtemps.


0 commentaires

1
votes

Vous ne mentionnez pas la langue que vous utilisez, je vais donc faire une déclaration générale sur le verrouillage. Le verrouillage est assez cher, en particulier le verrouillage naïf qui est originaire de nombreuses langues. Dans de nombreux cas, vous lisez une variable partagée (par opposition à la rédaction). La lecture est threadsafe tant qu'elle ne se déroule pas simultanément avec une écriture. Cependant, vous devez toujours le verrouiller. La forme la plus naïve de ce verrouillage consiste à traiter la lecture et l'écriture comme le même type d'opération, en limitant l'accès à la variable partagée d'autres lectures ainsi que les écritures. Un verrouillage de lecture / écrivain peut améliorer considérablement les performances. Un écrivain, des lecteurs infinis. Sur une application, j'ai travaillé, j'ai vu une amélioration de 35% de performance lors de la commutation de cette construction. Si vous travaillez dans .NET, le verrou correct est le readerWriterLocksLIM.


1 commentaires

Et en java c'est java.util.concurrent.locks.reentrantreadritelock



3
votes

Vous devriez d'abord être familiarisé avec Law AMDAHL .

Si vous utilisez Java, je recommande le livre Java Concurrence dans la pratique ; Cependant, la majeure partie de son aide est spécifique à la langue Java (Java 5 ou ultérieure).

En général, la réduction de la quantité de mémoire partagée augmente la quantité de parallélisme possible et pour la performance qui devrait être une considération majeure.

Le threading avec interface graphique est une autre chose à prendre conscience, mais on dirait que cela n'est pas pertinent pour ce problème particulier.


0 commentaires

1
votes

Je recommande de rechercher des processus multiples plutôt que de multiples threads dans le même processus, s'il s'agit d'une application de serveur.

L'avantage de la division du travail entre plusieurs processus sur une machine est qu'il est facile d'augmenter le nombre de serveurs lorsque plus de performances sont nécessaires qu'un seul serveur peut livrer.

Vous réduisez également les risques liés aux applications multithreadées complexes où des blocages, des goulots d'étranglement, etc. Réduisent la performance totale.

Il existe des cadres commerciaux qui simplifient le développement du logiciel serveur lorsqu'il s'agit de traitement de la file d'équilibrage de chargement et de la mise au point de votre propre infrastructure de partage de charge n'est pas si compliquée par rapport à ce que vous rencontrerez en général dans une application multi-threadée. < / p>


2 commentaires

Il y a beaucoup de "problèmes" aux nombreux processus APROCH. Tout d'abord, le fait que les processus ne partagent pas le même espace mémoire que les threads le font, je partage beaucoup d'informations entre les threads afin de passer des threads aux processus ne seront faciles pas du tout


Ne pas partager que la mémoire peut être un avantage en matière de cohache de cohache dans SMP, mais avec plusieurs cœurs, il est probablement préférable de partager la mémoire, telles que des recherches de table, des arbres de recherche, etc., cela dépend toujours de l'application, de la manière dont il est conçu, etc. A La règle générale est qu'il est très difficile de transformer une application à une seule-filetage en un multi-fileté sans avoir des problèmes ni insérer autant de verrous que son application pratiquement est une seule application filetée. Il doit être conçu pour être MT depuis le début.



1
votes

J'utilise Delphi 7

Vous pouvez utiliser des objets COM, ensuite, explicitement ou implicitement; Si vous êtes, les objets COM ont leurs propres complications et restrictions sur le threading: Processus, threads et appartements .


2 commentaires

Je ne suis pas, mais c'est bien de savoir.


Je n'ai pas utilisé DELPHI, mais je pensais que le VCL a été mis en œuvre à l'aide de COM: en.wikipedia.org/wiki / Visual_Component_Library - Même en dehors de cela, votre code multi-fileté et votre interface utilisateur devraient presque certainement être séparés les uns des autres.



0
votes

Les threads ne sont pas égaux de performance, toujours.

Les choses sont beaucoup meilleures dans certains systèmes d'exploitation, par opposition aux autres, mais si vous pouvez avoir quelque chose de dormir ou de renoncer à son temps jusqu'à ce qu'il soit signalé ... ou ne démarrez pas un nouveau processus pour pratiquement tout, vous vous économiserez de enlaver l'application dans la commutation contextuelle.


0 commentaires