Durée de sommeil optimale dans un modèle à plusieurs producteurs / consommateur unique

StackOverflow https://stackoverflow.com/questions/1400605

  •  05-07-2019
  •  | 
  •  

Question

Je suis en train d’écrire une application qui a un modèle consommateur multiple et producteur unique (plusieurs threads envoient des messages à un seul thread auteur de fichiers).

Chaque thread de production contient deux files d'attente, une pour l'écriture et l'autre pour la lecture. Chaque boucle du fil consommateur, elle parcourt chaque producteur et verrouille le mutex de ce producteur, échange les files d'attente, se déverrouille et écrit dans la file d'attente que le producteur n'utilise plus.

Dans la boucle du thread consommateur, il reste en veille après le traitement de tous les threads producteurs. Une chose que j’ai tout de suite remarquée, c’est que le temps moyen qu’un producteur écrit pour faire quelque chose dans la file d’attente et qui revenait augmentait considérablement (de 5 fois) lorsque je passais de 1 producteur à 2 sur un thread. dehors - il n'y a pas beaucoup de différence entre le temps pris avec 10 producteurs vs 15 producteurs. Ceci est probablement dû au fait que plus il y a de producteurs à traiter, moins il y a de conflits pour le mutex du producteur.

Malheureusement, ayant < 5 producteurs est un scénario assez courant pour l'application et j'aimerais optimiser le temps de sommeil afin d'obtenir des performances raisonnables, quel que soit le nombre de producteurs existants. J'ai remarqué qu'en augmentant le temps de sommeil, je pouvais obtenir de meilleures performances si le nombre de producteurs était faible, mais une performance plus médiocre pour les grands producteurs.

Quelqu'un d'autre a-t-il rencontré ce problème et si oui, quelle était votre solution? J'ai essayé de réduire le temps de sommeil en fonction du nombre de threads, mais cela semble plutôt spécifique à la machine et de jolis essais et erreurs.

Était-ce utile?

La solution

Vous pouvez choisir le temps de sommeil en fonction du nombre de producteurs ou même adapter le temps de sommeil en fonction d’un schéma dynamique. Si le consommateur se réveille sans travail, doublez le temps de sommeil, sinon divisez-le par deux. Mais contraignez le temps de sommeil à un minimum et à un maximum.

Dans les deux cas, vous abordez un problème plus fondamental. Dormir et voter est facile à obtenir et constitue parfois la seule approche disponible, mais elle présente de nombreux inconvénients et n’est pas la & "Right &"; manière.

Vous pouvez aller dans la bonne direction en ajoutant un sémaphore qui est incrémenté chaque fois qu'un producteur ajoute un élément à une file d'attente et décrémenté lorsque le consommateur traite un élément dans une file d'attente. Le consommateur ne se réveillera que s’il ya des articles à traiter et le fera immédiatement.

L’interrogation des files d’attente peut néanmoins poser problème. Vous pouvez ajouter une nouvelle file d'attente faisant référence à toute file d'attente contenant des éléments. Mais cela pose plutôt la question de savoir pourquoi vous n’avez pas une seule file d’attente que le consommateur traite plutôt qu’une file d’attente par producteur. Toutes choses égales par ailleurs, cela semble être la meilleure approche.

Autres conseils

Au lieu de dormir, je recommanderais à votre consommateur de bloquer sur une condition signalée par les producteurs. Sur un système compatible posix, vous pouvez le faire fonctionner avec pthread_cond. Créez un tableau de pthread_cond_t, un pour chaque producteur, puis créez-en un supplémentaire partagé entre eux. Les producteurs signalent d'abord leur variable de condition individuelle, puis la variable partagée. Le consommateur attend la condition partagée, puis itère sur les éléments du tableau, en effectuant un pthread_cond_timed_wait() sur chaque élément du tableau (utilisez pthread_get_expiration_np() pour obtenir le temps absolu pour & "Maintenant &"; ). Si l'attente renvoie 0, alors ce producteur a écrit des données. Le consommateur doit réinitialiser les variables de condition avant d’attendre à nouveau.

En utilisant les temps d'attente bloqués, vous réduisez au minimum le temps pendant lequel le consommateur verrouille inutilement les producteurs. Vous pouvez également utiliser cette méthode avec des sémaphores, comme indiqué dans une réponse précédente. Les sémaphores ont une sémantique simplifiée comparée aux conditions, à mon avis, mais vous devez être prudent pour décrémenter le sémaphore partagé une fois pour chaque producteur traité à chaque passage dans la boucle consommateur. Les variables de condition présentent l’avantage de pouvoir être utilisées comme des sémaphores booléens si vous les réinitialisez une fois qu’elles ont été signalées.

Essayez de trouver une implémentation d'une file d'attente de blocage dans le langage que vous utilisez pour la programmation. Une seule file d'attente suffira pour un nombre quelconque de producteurs et d'un consommateur.

Pour moi, il semblerait que vous introduisiez accidentellement une certaine mise en mémoire tampon en laissant le fil consommateur être occupé ailleurs, en veille ou en train de travailler. (la file faisant office de tampon) Peut-être qu'une simple mise en mémoire tampon du côté du producteur réduira vos conflits.

Il semble que votre système soit très sensible aux conflits de conflit entre le producteur et le consommateur, mais je suis perplexe quant à la raison pour laquelle une telle opération d'échange prendrait suffisamment de temps CPU pour apparaître dans vos statistiques d'exécution.

Pouvez-vous afficher du code?

modifier: vous prenez peut-être votre verrou et vous échangez les files d'attente même s'il n'y a pas de travail à faire?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top