57 votes

Quelle est la charge de travail lors de la création d'un fil ?

Je viens de revoir un code vraiment terrible - un code qui envoie des messages sur un port série en créant un nouveau thread pour emballer et assembler le message dans un nouveau thread pour chaque message envoyé. Oui, pour chaque message, un pthread est créé, les bits sont correctement configurés, puis le thread se termine. Je n'ai aucune idée de la raison pour laquelle quelqu'un ferait une telle chose, mais cela soulève la question suivante : quelle est la surcharge lors de la création d'un thread ?

0 votes

Un seul fil de travail permettra de résoudre certains des autres types de conflits de ressources que vous pourriez avoir avec plusieurs fils (écritures entrelacées, etc.).

0 votes

Je voudrais dire que oui, c'est une chose horrible à faire - ce qui m'amène à ma question, combien de frais généraux sont encourus lors de la création d'un fil (en général) Franchement, je ne sais pas comment déterminer ou même mesurer une implémentation de la bibliothèque pthread.

13 votes

Les frais généraux sont 0.37% si le message est de la bonne taille.

70voto

Nafnlaus Points 601

Pour ressusciter ce vieux fil de discussion, je viens de faire quelques tests de code simples :

#include <thread>

int main(int argc, char** argv)
{
  for (volatile int i = 0; i < 500000; i++)
    std::thread([](){}).detach();
  return 0;
}

Je l'ai compilé avec g++ test.cpp -std=c++11 -lpthread -O3 -o test . Je l'ai ensuite exécuté trois fois de suite sur un vieil ordinateur portable (noyau 2.6.18) très chargé (en train de reconstruire une base de données) et lent (Intel core i5-2540M). Résultats de trois exécutions consécutives : 5.647s, 5.515s, et 5.561s. Il s'agit donc d'un peu plus de 10 microsecondes par thread sur cette machine, probablement beaucoup moins sur la vôtre.

Ce n'est pas beaucoup de frais généraux, étant donné que les ports série ont une vitesse maximale d'environ 1 bit par 10 microsecondes. Maintenant, bien sûr, il y a diverses pertes de threads supplémentaires que l'on peut obtenir impliquant des arguments passés/capturés (bien que les appels de fonction eux-mêmes puissent en imposer), des ralentissements de cache entre les cœurs (si plusieurs threads sur différents cœurs se battent sur la même mémoire en même temps), etc. Mais en général, je doute fortement que le cas d'utilisation que vous avez présenté ait un impact négatif sur les performances (et pourrait même apporter des avantages), même si vous avez déjà qualifié le concept de "code vraiment terrible" sans même savoir combien de temps il faut pour lancer un thread.

Le fait que ce soit une bonne idée ou non dépend beaucoup des détails de votre situation. De quoi d'autre le fil conducteur est-il responsable ? Qu'est-ce qui est précisément impliqué dans la préparation et la rédaction des paquets ? À quelle fréquence sont-ils écrits (avec quel type de distribution ? uniforme, en grappes, etc... ?) et quelle est leur structure ? Combien de cœurs le système possède-t-il ? Etc. Selon les détails, la solution optimale pourrait être n'importe où, de "pas de threads du tout" à "un pool de threads partagé" à "un thread pour chaque paquet".

Notez que les pools de threads ne sont pas magiques et peuvent dans certains cas constituer un ralentissement par rapport aux threads uniques, car l'un des plus gros ralentissements avec les threads est la synchronisation de la mémoire cache utilisée par plusieurs threads en même temps, et les pools de threads, par leur nature même, doivent rechercher et traiter les mises à jour d'un autre thread. Ainsi, votre thread primaire ou votre thread de traitement enfant peut se retrouver coincé à devoir attendre si le processeur n'est pas sûr que l'autre processus ait modifié une section de la mémoire. En revanche, dans une situation idéale, un thread de traitement unique pour une tâche donnée ne doit partager la mémoire avec la tâche qui l'appelle qu'une seule fois (au moment de son lancement), puis ils n'interfèrent plus jamais l'un avec l'autre.

19voto

ubiquibacon Points 3212

On m'a toujours dit que la création de fils n'était pas chère, surtout si on la compare à la création d'un processus. Si le programme dont vous parlez ne comporte pas beaucoup d'opérations devant être exécutées simultanément, le threading n'est peut-être pas nécessaire, et à en juger par ce que vous avez écrit, cela pourrait bien être le cas. Un peu de littérature pour me conforter :

http://www.personal.kent.edu/~rmuhamma/OpSystems/Myos/threads.htm

Les fils sont bon marché dans le sens où

  1. Ils n'ont besoin que d'une pile et d'une mémoire pour les registres. Les fils sont donc peu coûteux à créer.

  2. Les threads utilisent très peu de ressources d'un système d'exploitation en dans lequel ils fonctionnent. C'est-à-dire que les threads n'ont pas besoin d'un nouvel espace d'adressage, de données globales, de code de programme ou de d'exploitation.

  3. Les changements de contexte sont rapides lorsqu'on travaille avec des threads. La raison en est raison est que nous devons seulement sauvegarder et/ou restaurer PC, SP et les registres.

Toujours la même chose aquí .

Sur Concepts des systèmes d'exploitation 8ème édition (page 155), les auteurs écrivent sur les avantages de l'enfilage :

L'allocation de mémoire et de ressources pour la création de processus est coûteuse. Parce que les threads partagent la ressource du processus processus auquel ils appartiennent, il est plus économique de créer et de de créer des threads et de les changer de contexte. Empiriquement la différence de surcharge peut être difficile à évaluer difficile, mais en général, il est beaucoup plus long de créer et de gérer des processus que de threads. Sur Solaris, par exemple, la création d'un processus est environ trente fois plus lente que la création d'un thread, et le et le changement de contexte est environ cinq fois plus lent.

14voto

ruslik Points 8442

La création de fils entraîne une certaine surcharge, mais si on la compare aux vitesses de transmission généralement lentes du port série (19200 bits/seconde étant la plus courante), elle n'a pas d'importance.

5 votes

Je suis d'accord. Pourquoi s'inquiéter du délai de quelques microsecondes pour créer un fil de discussion alors que la mise en réseau est susceptible de provoquer des délais de plusieurs dizaines de millisecondes, voire de secondes.

13voto

Tony D Points 43962

...envoie des messages sur un port série ... pour chaque message un pthread est créé, les bits sont correctement configurés, puis le thread se termine. ... combien d'overheads y a-t-il lors de la création d'un thread ?

Ceci est très spécifique au système. Par exemple, la dernière fois que j'ai utilisé VMS, le threading était d'une lenteur cauchemardesque (cela fait des années, mais de mémoire, un thread pouvait en créer quelque chose comme 10 de plus par seconde (et si vous mainteniez cela pendant quelques secondes sans que les threads ne sortent, vous auriez un noyau)), alors que sous Linux vous pouvez probablement en créer des milliers. Si vous voulez savoir exactement, faites un benchmark sur votre système. Mais cela ne sert pas à grand-chose de savoir cela sans en savoir plus sur les messages : qu'ils soient en moyenne de 5 octets ou de 100 000, qu'ils soient envoyés de manière contiguë ou que la ligne reste inactive entre les deux, et quelles sont les exigences de latence pour l'application, tout cela est aussi pertinent pour l'utilisation appropriée des threads du code que n'importe quelle mesure absolue de l'overhead de création de threads. Et les performances n'ont peut-être pas besoin d'être la considération dominante de la conception.

12voto

Michael Goldshteyn Points 24679

Vous ne voulez surtout pas faire ça. Créez un seul thread ou un pool de threads et signalez simplement lorsque des messages sont disponibles. À la réception du signal, le thread peut effectuer tout traitement nécessaire des messages.

En termes de frais généraux, la création/destruction de threads, en particulier sous Windows, est assez coûteuse. De l'ordre de quelques dizaines de microsecondes, pour être précis. Elle ne devrait, pour la plupart, être effectuée qu'au début/à la fin d'une application, à l'exception peut-être des pools de threads redimensionnés dynamiquement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X