Comment demander à une machine multicœur/multi-CPU de traiter les appels de fonction en boucle en parallèle ?

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

  •  09-06-2019
  •  | 
  •  

Question

Je conçois actuellement une application dotée d'un module qui chargera de grandes quantités de données à partir d'une base de données et les réduira à un ensemble beaucoup plus petit par divers calculs en fonction des circonstances.

Bon nombre des opérations les plus intensives se comportent de manière déterministe et se prêteraient à un traitement parallèle.

À condition d'avoir une boucle qui parcourt un grand nombre de morceaux de données arrivant de la base de données et pour chacun d'eux d'appeler une fonction déterministe sans effets secondaires, comment puis-je faire en sorte que le programme n'attende pas le retour de la fonction mais définit plutôt les prochains appels en cours, pour qu'ils puissent être traités en parallèle ?Une approche naïve pour démontrer le principe me suffirait pour l’instant.

J'ai lu l'article MapReduce de Google et même si je pourrais utiliser le principe général à plusieurs endroits, je ne ciblerai pas, pour l'instant, les grands clusters, il s'agira plutôt d'une seule machine multicœur ou multi-CPU pour la version 1.0. .Donc, actuellement, je ne sais pas si je peux réellement utiliser la bibliothèque ou si je devrais lancer moi-même une version de base simplifiée.

Je suis à un stade précoce du processus de conception et jusqu'à présent, je cible C-something (pour les bits critiques en termes de vitesse) et Python (pour les bits critiques en productivité) comme langages.S’il y a des raisons impérieuses, je pourrais changer, mais pour l’instant je suis satisfait de mon choix.

Veuillez noter que je suis conscient du fait que la récupération du morceau suivant de la base de données peut prendre plus de temps que le traitement du morceau actuel et que l'ensemble du processus serait alors lié aux E/S.Je suppose cependant pour l'instant que ce n'est pas le cas et, en pratique, j'utilise un cluster de base de données, une mise en cache mémoire ou autre chose pour ne pas être lié aux E/S à ce stade.

Était-ce utile?

La solution

Il me manque peut-être quelque chose ici, mais cela semble assez simple en utilisant pthreads.

Configurez un petit pool de threads contenant N threads et disposez d’un thread pour les contrôler tous.

Le thread maître reste simplement dans une boucle et fait quelque chose comme :

  1. Récupérer un morceau de données de la base de données
  2. Trouver le prochain sujet gratuit Si aucun sujet n'est libre, attendez
  3. Remettre le morceau au thread de travail
  4. Revenez en arrière et récupérez le prochain morceau de DB

Pendant ce temps, les threads de travail s'assoient et font :

  1. Me marquer comme libre
  2. Attendez que le fil de discussion me donne un morceau de données
  3. Traiter le bloc de données
  4. Me marquer à nouveau comme libre

La méthode par laquelle vous implémentez cela peut être aussi simple que deux tableaux contrôlés par mutex.L'un contient les threads travaillés (le pool de threads) et l'autre indique si chaque thread correspondant est libre ou occupé.

Ajustez N à votre guise...

Autres conseils

Eh bien, si .net est une option, ils ont déployé beaucoup d'efforts pour y parvenir. Traitement en parallèle.

Si vous prévoyez toujours d'utiliser Python, vous voudrez peut-être jeter un œil à Traitement.Il utilise des processus plutôt que des threads pour le calcul parallèle (en raison du Python GIL) et fournit des classes pour distribuer des « éléments de travail » sur plusieurs processus.À l’aide de la classe pool, vous pouvez écrire du code comme celui-ci :

import processing

def worker(i):
    return i*i
num_workers = 2
pool = processing.Pool(num_workers)
result = pool.imap(worker, range(100000))

Il s'agit d'une version parallèle de itertools.imap, qui distribue les appels aux processus.Vous pouvez également utiliser les méthodes apply_async du pool et stocker les objets de résultat paresseux dans une liste :

results = []
for i in range(10000):
    results.append(pool.apply_async(worker, i))

Pour plus de référence, voir la documentation de la classe Pool.

Pièges :

  • le traitement utilise fork(), il faut donc être prudent sur Win32
  • les objets transférés entre les processus doivent pouvoir être décapés
  • si les travailleurs sont relativement rapides, vous pouvez modifier la taille des morceaux, c'est-à-direle nombre d'éléments de travail envoyés à un processus de travail dans un lot
  • Processing.Pool utilise un thread d'arrière-plan

Vous pouvez implémenter l'algorithme de Google CarteRéduire sans avoir de machines physiquement séparées.Considérez simplement chacun de ces "machines" comme des "threads". Les threads sont automatiquement distribués sur des machines multi-core.

Si vous travaillez avec un compilateur qui le prend en charge, je vous suggère de jeter un œil à http://www.openmp.org Pour un moyen d'annoter votre code de telle manière que certaines boucles seront parallélisées.

Il fait également beaucoup plus et cela pourrait vous être très utile.

Leur page Web indique que gcc4.2 prendra en charge openmp, par exemple.

Le même pool de threads est utilisé en Java.Mais les threads des pools de threads sont sérialisables, envoyés à d'autres ordinateurs et désérialisés pour s'exécuter.

J'ai développé une bibliothèque MapReduce pour une utilisation multi-thread/multi-core sur un seul serveur.Tout est pris en charge par la bibliothèque, et l'utilisateur n'a plus qu'à implémenter Map et Reduction.Elle se positionne comme une bibliothèque Boost, mais n'est pas encore acceptée comme bibliothèque formelle.Vérifier http://www.craighenderson.co.uk/mapreduce

Vous pourriez être intéressé à examiner le code de libdispatch, qui est l'implémentation open source de Grand Central Dispatch d'Apple.

Le TBB ou boost::mpi d'Intel pourrait également vous intéresser.

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