Les processus enfants générés via le multitraitement partagent-ils des objets créés précédemment dans le programme?
J'ai la configuration suivante:
do_some_processing(filename):
for line in file(filename):
if line.split(',')[0] in big_lookup_object:
# something here
if __name__ == '__main__':
big_lookup_object = marshal.load('file.bin')
pool = Pool(processes=4)
print pool.map(do_some_processing, glob.glob('*.data'))
Je charge un gros objet en mémoire, puis je crée un pool de travailleurs qui doivent utiliser ce gros objet. Le gros objet est accessible en lecture seule, je n'ai pas besoin de passer des modifications entre les processus.
Ma question est la suivante: le gros objet est-il chargé dans la mémoire partagée, comme ce serait le cas si je créais un processus sous unix / c, ou chaque processus charge-t-il sa propre copie du gros objet?
Mise à jour: pour clarifier davantage - big_lookup_object est un objet de recherche partagé. Je n'ai pas besoin de diviser cela et de le traiter séparément. Je dois en garder un seul exemplaire. Le travail dont j'ai besoin pour le fractionner consiste à lire de nombreux autres fichiers volumineux et à rechercher les éléments de ces fichiers volumineux par rapport à l'objet de recherche.
Mise à jour supplémentaire: la base de données est une bonne solution, memcached pourrait être une meilleure solution et le fichier sur disque (shelve ou dbm) pourrait être encore meilleur. Dans cette question, j'étais particulièrement intéressé par une solution en mémoire. Pour la solution finale, j'utiliserai hadoop, mais je voulais voir si je pouvais également avoir une version locale en mémoire.
la source
marshal.load
parent et pour chaque enfant (chaque processus importe le module).Réponses:
"Les processus enfants générés via le multi-traitement partagent-ils des objets créés plus tôt dans le programme?"
Non (python avant la version 3.8), et Oui dans la version 3.8 ( https://docs.python.org/3/library/multiprocessing.shared_memory.html#module-multiprocessing.shared_memory )
Les processus ont un espace mémoire indépendant.
Solution 1
Pour tirer le meilleur parti d'une grande structure avec beaucoup de travailleurs, procédez comme suit.
Ecrire chaque travailleur comme un "filtre" - lit les résultats intermédiaires de stdin, fonctionne, écrit les résultats intermédiaires sur stdout.
Connectez tous les travailleurs en tant que pipeline:
Chaque processus lit, travaille et écrit.
Ceci est remarquablement efficace car tous les processus sont exécutés simultanément. Les écritures et les lectures passent directement par des tampons partagés entre les processus.
Solution 2
Dans certains cas, vous avez une structure plus complexe - souvent une structure «en éventail». Dans ce cas, vous avez un parent avec plusieurs enfants.
Parent ouvre les données source. Le parent fourche un certain nombre d'enfants.
Parent lit la source, ferme des parties de la source sur chaque enfant exécuté simultanément.
Lorsque le parent atteint la fin, fermez le tuyau. L'enfant obtient la fin du fichier et se termine normalement.
Les parties enfants sont agréables à écrire car chaque enfant lit simplement
sys.stdin
.Le parent a un peu de fantaisie pour engendrer tous les enfants et retenir correctement les tuyaux, mais ce n'est pas trop mal.
Fan-in est la structure opposée. Un certain nombre de processus exécutés indépendamment doivent entrelacer leurs entrées dans un processus commun. Le collecteur n'est pas aussi facile à écrire, car il doit lire à partir de nombreuses sources.
La lecture de nombreux tubes nommés est souvent effectuée à l'aide du
select
module pour voir quels tubes ont une entrée en attente.Solution 3
La recherche partagée est la définition d'une base de données.
Solution 3A - charger une base de données. Laissez les travailleurs traiter les données de la base de données.
Solution 3B - créez un serveur très simple en utilisant werkzeug (ou similaire) pour fournir des applications WSGI qui répondent à HTTP GET afin que les travailleurs puissent interroger le serveur.
Solution 4
Objet du système de fichiers partagé. Unix OS propose des objets de mémoire partagée. Ce ne sont que des fichiers qui sont mappés à la mémoire afin que l'échange d'E / S soit effectué au lieu de lectures tamponnées plus conventionnelles.
Vous pouvez le faire à partir d'un contexte Python de plusieurs manières
Écrivez un programme de démarrage qui (1) divise votre objet gigantesque original en objets plus petits, et (2) démarre les travailleurs, chacun avec un objet plus petit. Les objets plus petits pourraient être des objets Python marinés pour économiser un tout petit peu de temps de lecture de fichier.
Écrivez un programme de démarrage qui (1) lit votre objet gigantesque original et écrit un fichier structuré en page et codé en octets en utilisant des
seek
opérations pour vous assurer que les sections individuelles sont faciles à trouver avec de simples recherches. C'est ce que fait un moteur de base de données - diviser les données en pages, rendre chaque page facile à localiser via un fichierseek
.Créez des ouvriers ayant accès à ce grand fichier structuré en page. Chaque travailleur peut rechercher les pièces pertinentes et y effectuer son travail.
la source
Les processus enfants générés via le multitraitement partagent-ils des objets créés précédemment dans le programme?
Ça dépend. Pour les variables globales en lecture seule, cela peut souvent être considéré comme tel (à part la mémoire consommée), sinon il ne devrait pas.
La documentation du multitraitement dit:
Better to inherit than pickle/unpickle
Explicitly pass resources to child processes
Global variables
Exemple
Sous Windows (processeur unique):
Avec
sleep
:Sans
sleep
:la source
z
n'est pas partagé. Cela répond donc à la question: "non, sous Windows au moins, une variable parent n'est pas partagée entre les enfants".z
cas) elles peuvent être considérées comme partagées.S.Lott a raison. Les raccourcis multitraitement de Python vous donnent effectivement un morceau de mémoire séparé et dupliqué.
Sur la plupart des systèmes * nix, utiliser un appel à un niveau inférieur
os.fork()
vous donnera en fait une mémoire de copie sur écriture, ce que vous pensez peut-être. AFAIK, en théorie, dans le programme le plus simpliste possible, vous pouvez lire ces données sans les dupliquer.Cependant, les choses ne sont pas aussi simples dans l'interpréteur Python. Les données d'objet et les méta-données sont stockées dans le même segment de mémoire, donc même si l'objet ne change jamais, quelque chose comme un compteur de référence pour cet objet incrémenté provoquera une écriture mémoire, et donc une copie. Presque tous les programmes Python qui font plus que "imprimer" bonjour "" entraîneront des incréments du nombre de références, donc vous ne réaliserez probablement jamais l'avantage de la copie sur écriture.
Même si quelqu'un réussissait à pirater une solution de mémoire partagée en Python, essayer de coordonner le ramassage des ordures entre les processus serait probablement assez pénible.
la source
Si vous utilisez Unix, ils peuvent partager le même objet, en raison du fonctionnement de fork (c'est-à-dire que les processus enfants ont une mémoire séparée mais c'est une copie en écriture, donc elle peut être partagée tant que personne ne la modifie). J'ai essayé ce qui suit:
et a obtenu la sortie suivante:
Bien sûr, cela ne prouve pas qu'une copie n'a pas été faite, mais vous devriez pouvoir le vérifier dans votre situation en regardant la sortie de
ps
pour voir la quantité de mémoire réelle utilisée par chaque sous-processus.la source
Différents processus ont un espace d'adressage différent. Comme exécuter différentes instances de l'interpréteur. C'est à cela que sert l'IPC (communication interprocessus).
Vous pouvez utiliser des files d'attente ou des tuyaux à cette fin. Vous pouvez également utiliser rpc sur tcp si vous souhaitez distribuer les processus sur un réseau ultérieurement.
http://docs.python.org/dev/library/multiprocessing.html#exchanging-objects-between-processes
la source
Pas directement lié au multitraitement en soi, mais d'après votre exemple, il semblerait que vous puissiez simplement utiliser le module shelve ou quelque chose comme ça. Le "big_lookup_object" doit-il vraiment être complètement en mémoire?
la source
Non, mais vous pouvez charger vos données en tant que processus enfant et lui permettre de partager ses données avec d'autres enfants. voir ci-dessous.
la source
Pour la plate-forme Linux / Unix / MacOS, forkmap est une solution rapide et sale.
la source