web-dev-qa-db-fra.com

Les données partagées en lecture seule sont-elles copiées dans différents processus pour le multitraitement?

Le morceau de code que j'ai ressemble à ceci:

glbl_array = # a 3 Gb array

def my_func( args, def_param = glbl_array):
    #do stuff on args and def_param

if __== '__main__':
  pool = Pool(processes=4)
  pool.map(my_func, range(1000))

Existe-t-il un moyen de s'assurer (ou d'encourager) que les différents processus n'obtiennent pas une copie de glbl_array mais la partagent. S'il n'y a aucun moyen d'arrêter la copie, j'irai avec un tableau memmappé, mais mes modèles d'accès ne sont pas très réguliers, donc je m'attends à ce que les tableaux memmappés soient plus lents. Ce qui précède semblait être la première chose à essayer. C'est sous Linux. Je voulais juste quelques conseils de Stackoverflow et je ne veux pas ennuyer le sysadmin. Pensez-vous que cela aidera si le second paramètre est un véritable objet immuable comme glbl_array.tostring().

50
san

Vous pouvez utiliser assez facilement la mémoire partagée de multiprocessing avec Numpy:

import multiprocessing
import ctypes
import numpy as np

shared_array_base = multiprocessing.Array(ctypes.c_double, 10*10)
shared_array = np.ctypeslib.as_array(shared_array_base.get_obj())
shared_array = shared_array.reshape(10, 10)

#-- edited 2015-05-01: the assert check below checks the wrong thing
#   with recent versions of Numpy/multiprocessing. That no copy is made
#   is indicated by the fact that the program prints the output shown below.
## No copy was made
##assert shared_array.base.base is shared_array_base.get_obj()

# Parallel processing
def my_func(i, def_param=shared_array):
    shared_array[i,:] = i

if __== '__main__':
    pool = multiprocessing.Pool(processes=4)
    pool.map(my_func, range(10))

    print shared_array

qui imprime

[[ 0.  0.  0.  0.  0.  0.  0.  0.  0.  0.]
 [ 1.  1.  1.  1.  1.  1.  1.  1.  1.  1.]
 [ 2.  2.  2.  2.  2.  2.  2.  2.  2.  2.]
 [ 3.  3.  3.  3.  3.  3.  3.  3.  3.  3.]
 [ 4.  4.  4.  4.  4.  4.  4.  4.  4.  4.]
 [ 5.  5.  5.  5.  5.  5.  5.  5.  5.  5.]
 [ 6.  6.  6.  6.  6.  6.  6.  6.  6.  6.]
 [ 7.  7.  7.  7.  7.  7.  7.  7.  7.  7.]
 [ 8.  8.  8.  8.  8.  8.  8.  8.  8.  8.]
 [ 9.  9.  9.  9.  9.  9.  9.  9.  9.  9.]]

Cependant, Linux a une sémantique de copie sur écriture sur fork(), donc même sans utiliser multiprocessing.Array, Les données ne seront pas copiées sauf si elles sont écrites.

106
pv.

Le code suivant fonctionne sur Win7 et Mac (peut-être sur Linux, mais non testé).

import multiprocessing
import ctypes
import numpy as np

#-- edited 2015-05-01: the assert check below checks the wrong thing
#   with recent versions of Numpy/multiprocessing. That no copy is made
#   is indicated by the fact that the program prints the output shown below.
## No copy was made
##assert shared_array.base.base is shared_array_base.get_obj()

shared_array = None

def init(shared_array_base):
    global shared_array
    shared_array = np.ctypeslib.as_array(shared_array_base.get_obj())
    shared_array = shared_array.reshape(10, 10)

# Parallel processing
def my_func(i):
    shared_array[i, :] = i

if __== '__main__':
    shared_array_base = multiprocessing.Array(ctypes.c_double, 10*10)

    pool = multiprocessing.Pool(processes=4, initializer=init, initargs=(shared_array_base,))
    pool.map(my_func, range(10))

    shared_array = np.ctypeslib.as_array(shared_array_base.get_obj())
    shared_array = shared_array.reshape(10, 10)
    print shared_array
7
taku-y

Pour ceux qui sont bloqués avec Windows, qui ne prend pas en charge fork() (à moins d'utiliser CygWin), la réponse de pv ne fonctionne pas. Les globaux ne sont pas mis à la disposition des processus enfants.

Au lieu de cela, vous devez transmettre la mémoire partagée lors de l'initialisation de Pool/Process comme telle:

#! /usr/bin/python

import time

from multiprocessing import Process, Queue, Array

def f(q,a):
    m = q.get()
    print m
    print a[0], a[1], a[2]
    m = q.get()
    print m
    print a[0], a[1], a[2]

if __== '__main__':
    a = Array('B', (1, 2, 3), lock=False)
    q = Queue()
    p = Process(target=f, args=(q,a))
    p.start()
    q.put([1, 2, 3])
    time.sleep(1)
    a[0:3] = (4, 5, 6)
    q.put([4, 5, 6])
    p.join()

(ce n'est pas numpy et ce n'est pas du bon code mais ça illustre le point ;-)

3
Brian White

Si vous recherchez une option qui fonctionne efficacement sous Windows et fonctionne bien pour les modèles d'accès irréguliers, les branchements et d'autres scénarios dans lesquels vous devrez peut-être analyser différentes matrices en fonction d'une combinaison d'une matrice de mémoire partagée et de données locales de processus, la boîte à outils mathDict du package ParallelRegression a été conçue pour gérer cette situation exacte.

1
RichardB