J'essaie de résoudre un gros problème numérique qui implique de nombreux sous-problèmes, et j'utilise le module de multiprocessing de Python (en particulier Pool.map) pour diviser différents sous-problèmes indépendants sur différents cœurs. Chaque sous-problème implique le calcul de nombreux sous-sous-problèmes, et j'essaie de mémoriser efficacement ces résultats en les stockant dans un fichier s'ils n'ont pas encore été calculés par un processus, sinon sautez le calcul et lisez simplement les résultats du fichier.
Je rencontre des problèmes de concurrence avec les fichiers: différents processus vérifient parfois si un sous-sous-problème a encore été calculé (en recherchant le fichier où les résultats seraient stockés), vérifiez qu'il ne l'est pas, exécutez le calcul, puis essayez d'écrire les résultats dans le même fichier en même temps. Comment éviter d'écrire des collisions comme celle-ci?
@ GP89 a mentionné une bonne solution. Utilisez une file d'attente pour envoyer les tâches d'écriture à un processus dédié qui a le seul accès en écriture au fichier. Tous les autres travailleurs ont un accès en lecture seule. Cela éliminera les collisions. Voici un exemple qui utilise apply_async, mais cela fonctionnera également avec la carte:
import multiprocessing as mp
import time
fn = 'c:/temp/temp.txt'
def worker(arg, q):
'''stupidly simulates long running process'''
start = time.clock()
s = 'this is a test'
txt = s
for i in range(200000):
txt += s
done = time.clock() - start
with open(fn, 'rb') as f:
size = len(f.read())
res = 'Process' + str(arg), str(size), done
q.put(res)
return res
def listener(q):
'''listens for messages on the q, writes to file. '''
with open(fn, 'w') as f:
while 1:
m = q.get()
if m == 'kill':
f.write('killed')
break
f.write(str(m) + '\n')
f.flush()
def main():
#must use Manager queue here, or will not work
manager = mp.Manager()
q = manager.Queue()
pool = mp.Pool(mp.cpu_count() + 2)
#put listener to work first
watcher = pool.apply_async(listener, (q,))
#fire off workers
jobs = []
for i in range(80):
job = pool.apply_async(worker, (i, q))
jobs.append(job)
# collect results from the workers through the pool result queue
for job in jobs:
job.get()
#now we are done, kill the listener
q.put('kill')
pool.close()
pool.join()
if __== "__main__":
main()
Il me semble que vous devez utiliser Manager
pour enregistrer temporairement vos résultats dans une liste, puis écrire les résultats de la liste dans un fichier. Utilisez également starmap
pour passer l'objet que vous souhaitez traiter et la liste gérée. La première étape consiste à créer le paramètre à transmettre à starmap
, qui inclut la liste gérée.
from multiprocessing import Manager
from multiprocessing import Pool
import pandas as pd
def worker(row, param):
# do something here and then append it to row
x = param**2
row.append(x)
if __== '__main__':
pool_parameter = [] # list of objects to process
with Manager() as mgr:
row = mgr.list([])
# build list of parameters to send to starmap
for param in pool_parameter:
params.append([row,param])
with Pool() as p:
p.starmap(worker, params)
À partir de ce moment, vous devez décider comment vous allez gérer la liste. Si vous avez des tonnes de RAM et un énorme ensemble de données, n'hésitez pas à concaténer en utilisant des pandas. Ensuite, vous pouvez enregistrer le fichier très facilement en tant que csv ou cornichon.
df = pd.concat(row, ignore_index=True)
df.to_pickle('data.pickle')
df.to_csv('data.csv')