Tengo una muy grande (sólo lectura) de la matriz de datos que quiero ser procesado por varios procesos en paralelo.

Me gusta la Piscina.función de mapa y me gustaría usarla para calcular funciones en los datos en paralelo.

Vi que uno puede usar el Valor o en la clase Array para uso compartido de los datos de la memoria entre procesos. Pero cuando trato de usar este tengo un RuntimeError: ‘SynchronizedString objetos sólo debe ser compartida entre procesos a través de la herencia cuando el uso de la Piscina.función de mapa:

Aquí es un ejemplo simplificado de lo que estoy tratando de hacer:

from sys import stdin
from multiprocessing import Pool, Array

def count_it( arr, key ):
  count = 0
  for c in arr:
    if c == key:
      count += 1
  return count

if __name__ == '__main__':
  testData = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
  # want to share it using shared memory
  toShare = Array('c', testData)

  # this works
  print count_it( toShare, "a" )

  pool = Pool()

  # RuntimeError here
  print pool.map( count_it, [(toShare,key) for key in ["a", "b", "s", "d"]] )

¿Alguien puede decirme qué estoy haciendo mal aquí?

Así que lo que me gustaría hacer es pasar información acerca de un recién creado memoria compartida asignada de la matriz de los procesos después de que se han creado en el proceso de la piscina.

  • Lamentablemente eso no es posible. La forma recomendada de acuerdo a los diputados de la documentación es el uso de inheritence (en la horquilla de las plataformas). Para sólo lectura de datos como la que tenemos aquí uno normalmente se utiliza un mundial, pero se puede utilizar una Matriz compartida para la lectura/escritura de la comunicación. Fork es barato, así que usted puede volver a la Piscina cada vez que reciba los datos, a continuación, cerrar después. Por desgracia, en Windows esto no es posible – la solución es utilizar una memoria compartida, Array (incluso en el caso de sólo lectura), pero esto sólo puede ser transmitida a los subprocesos en el proceso de creación (me imagino que deben ser agregados a la lista de acceso…
  • para el segmento de memoria compartida y que esta lógica no es aplicada, salvo en el subproceso de inicio). Se pueden pasar los datos compartidos matriz en el comienzo de la Piscina como he mostrado, o a un Proceso, de una manera similar. No se puede pasar de una memoria compartida Matriz a una Piscina abierta, tendrá que crear la Piscina después de la memoria. Fácil maneras de evitar esto incluye la asignación de un tamaño máximo de búfer, o simplemente la asignación de la matriz cuando usted sabe el tamaño requerido antes de comenzar la Piscina. Si usted mantiene sus variables globales de la Piscina no debe ser demasiado caro en windows – variables globales son automáticamente …
  • en vinagre y se envía a los procesos – que es por eso que mi propuesta de hacer un búfer de tamaño suficiente al inicio (donde es de esperar que su cantidad de variables globales es pequeña), luego a la Piscina, es mejor. Me tomé el tiempo para entender y resolver su problema, de buena fe, antes de editar su pregunta, por lo que aunque entiendo que si quieres que se ejecute, espero que al final se va a considerar la aceptación de mi respuesta si nada sustancialmente diferente/mejor viene.
  • Tuve un vistazo al código fuente y la información acerca de la memoria compartida puede ser en escabeche (necesario para obtener información acerca del proceso de cliente en windows), pero que el código tiene una aserción que sólo se ejecuta durante el proceso de desove. Me pregunto por qué.
InformationsquelleAutor Jeroen Dirks | 2009-11-04

4 Comentarios

  1. 41

    De intentar de nuevo como acabo de ver la recompensa 😉

    Básicamente creo que el mensaje de error significa lo que dijo – multiprocesadores de memoria compartida las Matrices no se pueden pasar como argumentos (por decapado). No tiene sentido para serialise los datos – el punto es que los datos de la memoria compartida. Así que usted tiene que hacer la matriz compartida global. Creo que es más claro que es el atributo de un módulo, como en mi primera respuesta, pero acaba de salir como una variable global en su ejemplo, también funciona bien. Tomando en cuenta su punto de no querer establecer los datos antes de la bifurcación, aquí es un ejemplo modificado. Si querían tener más de una posible matriz compartida (y que es la razón por la que quería pasar a compartir: como un argumento) que podría llevar a una lista global de matrices compartidas, y acaba de pasar el índice de count_it (que se convertiría en for c in toShare[i]:).

    from sys import stdin
    from multiprocessing import Pool, Array, Process
    
    def count_it( key ):
      count = 0
      for c in toShare:
        if c == key:
          count += 1
      return count
    
    if __name__ == '__main__':
      # allocate shared array - want lock=False in this case since we 
      # aren't writing to it and want to allow multiple processes to access
      # at the same time - I think with lock=True there would be little or 
      # no speedup
      maxLength = 50
      toShare = Array('c', maxLength, lock=False)
    
      # fork
      pool = Pool()
    
      # can set data after fork
      testData = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
      if len(testData) > maxLength:
          raise ValueError, "Shared array too small to hold data"
      toShare[:len(testData)] = testData
    
      print pool.map( count_it, ["a", "b", "s", "d"] )

    [EDITAR: El de arriba no funciona en windows, porque de no usar el tenedor. Sin embargo, los de abajo no funciona en Windows, todavía el uso de la Piscina, así que creo que este es el más cercano a lo que usted desea:

    from sys import stdin
    from multiprocessing import Pool, Array, Process
    import mymodule
    
    def count_it( key ):
      count = 0
      for c in mymodule.toShare:
        if c == key:
          count += 1
      return count
    
    def initProcess(share):
      mymodule.toShare = share
    
    if __name__ == '__main__':
      # allocate shared array - want lock=False in this case since we 
      # aren't writing to it and want to allow multiple processes to access
      # at the same time - I think with lock=True there would be little or 
      # no speedup
      maxLength = 50
      toShare = Array('c', maxLength, lock=False)
    
      # fork
      pool = Pool(initializer=initProcess,initargs=(toShare,))
    
      # can set data after fork
      testData = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
      if len(testData) > maxLength:
          raise ValueError, "Shared array too small to hold data"
      toShare[:len(testData)] = testData
    
      print pool.map( count_it, ["a", "b", "s", "d"] )

    No sé por qué mapa no se Adoban la matriz, pero el Proceso y la Piscina le – creo que tal vez haya cedido en el punto del subproceso de inicialización de windows. Tenga en cuenta que los datos todavía se establece después de que el tenedor sin embargo.

    • Incluso en plataformas con un tenedor no puede insertar nuevos datos compartidos en compartir: después de la horquilla, ya que cada proceso tiene su propia copia independiente en ese punto.
    • Así que el verdadero problema parece ser que cómo podemos pickle la información acerca de una Matriz de modo que se puede enviar y conectado en el otro proceso.
    • no eso no es correcto. La matriz tiene que ser establecido antes de la bifurcación, pero luego es de memoria compartida que puede ser cambiado, con cambios visibles en todos los niños. Fíjate en el ejemplo – puse los datos en el array después la horquilla (que occure cuando Piscina() se crea una instancia). Que los datos pueden ser obtenidos en tiempo de ejecución, después de que el tenedor, y siempre y cuando quepa en el preasignados segmento de memoria compartida puede ser copiado allí y he visto de todos los niños.
    • Usted puede pickle la Matriz, pero no el uso de la Piscina.
    • Editado para añadir el trabajo de la versión de Windows, sólo el uso de la Piscina (por el paso de la matriz compartida como un initiliazation parámetro.
    • Está cada vez más cerca, pero todavía hay el problema de que el compartir: matriz de longitud tiene que ser corregido antes de que la piscina es creado. Así que usted todavía está creando el segmento de memoria compartida antes de que los procesos se crean. Lo que realmente quiero ver como una solución general es una manera de crear una variable nueva longitud de la matriz compartida después de la piscina es creado, pasar info sobre ella en el proceso de trabajo y tener que leer de ella.
    • Me temo que no es posible con Piscina. Tienes que crear la memoria compartida de antemano.
    • En cualquier caso, no parece artificial requisito. Si el nuevo conjunto de datos es el tamaño adecuado para la actual búfer compartido – usted puede simplemente cerrar la piscina (pool.close()), crear una nueva matriz compartida del tamaño requerido y abrir un nuevo grupo. Para cualquier tareas de cálculo, donde el uso de multiprocesamiento la pena la sobrecarga de cierre y apertura de la piscina va a ser pequeña. Y la Piscina operaciones son relativamente atómica, por lo que no es como usted podría inyectar nuevos datos en el medio de un mapa de comandos.
    • La aserción en el decapado de los datos compartidos de la matriz parece ser artificial restricción sobre el uso de los recursos compartidos con multi-procesamiento, pero dado que la restricción que usted ha proporcionado algunas soluciones razonables, así que voy a dar los puntos por aceptada respuesta.

  2. 5

    El problema que yo veo es que la Piscina no admite el decapado de datos compartidos a través de su lista de argumentos. Eso es lo que el mensaje de error significa por «objetos sólo debe ser compartida entre procesos a través de la herencia». Los datos compartidos necesita ser hereditaria, es decir, global si quieres compartirla con la Piscina de la clase.

    Si usted necesita para pasar de forma explícita, puede que tenga que utilizar el multiprocesamiento.Proceso. Aquí está su reelaborado ejemplo:

    from multiprocessing import Process, Array, Queue
    
    def count_it( q, arr, key ):
      count = 0
      for c in arr:
        if c == key:
          count += 1
      q.put((key, count))
    
    if __name__ == '__main__':
      testData = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
      # want to share it using shared memory
      toShare = Array('c', testData)
    
      q = Queue()
      keys = ['a', 'b', 's', 'd']
      workers = [Process(target=count_it, args = (q, toShare, key))
        for key in keys]
    
      for p in workers:
        p.start()
      for p in workers:
        p.join()
      while not q.empty():
        print q.get(),

    De salida: (‘s’, 9) (‘a’, 2) (‘b’, 3)
    (‘d’, 12)

    El ordenamiento de los elementos de la cola puede variar.

    Para hacerlo más genérico y similar a la Piscina, se podría crear un fijo N el número de Procesos, dividir la lista de claves en N trozos, y luego usar una función de contenedor como el Proceso de destino, que se llame count_it para cada una de las teclas en la lista que se pasa, como:

    def wrapper( q, arr, keys ):
      for k in keys:
        count_it(q, arr, k)
  3. 2

    Si los datos es de sólo lectura sólo tienes que hacer una variable en un módulo de antes de la bifurcación de la Piscina. A continuación, todos los procesos hijo debe ser capaz de acceder a él, y no va a ser copiado siempre y cuando usted no escribir en él.

    import myglobals # anything (empty .py file)
    myglobals.data = []
    
    def count_it( key ):
        count = 0
        for c in myglobals.data:
            if c == key:
                count += 1
        return count
    
    if __name__ == '__main__':
    myglobals.data = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
    
    pool = Pool()
    print pool.map( count_it, ["a", "b", "s", "d"] )

    Si usted desea intentar el uso de la Matriz, aunque se podría intentar con el lock=False palabra clave argumento (es true de forma predeterminada).

    • No creo que el uso de variables globales es seguro y no funciona en windows, donde los procesos no son bifurcadas.
    • ¿Cómo es que no es seguro? Si sólo necesita acceso de lectura a los datos que está bien. Si usted escribe para él, por error, a continuación, la versión modificada de la página se copia en escritura para el proceso hijo para que nada malo va a pasar (no interferir con otros procesos, por ejemplo). Tienes razón al decir que no funciona en windows, aunque…
    • Tienes razón en que es seguro en la horquilla basados en plataformas. Pero me gustaría saber si hay una memoria compartida, basada en una forma de compartir grandes cantidades de datos después de que el proceso de la piscina es creado.
  4. -1

    El multiprocesamiento.sharedctypes módulo proporciona funciones para la asignación de ctypes objetos de memoria compartida que puede ser heredado por los procesos hijos.

    Por lo que su uso de sharedctypes está mal. ¿Desea heredar esta matriz de proceso primario o prefieres pasar explícitamente? En el primer caso, usted tiene que crear una variable global como otras respuestas sugieren. Pero usted no necesita utilizar sharedctypes para pasar de forma explícita, acaba de pasar original testData.

    IVA, el uso de Pool.map() está mal. Tiene la misma interfaz que builtin map() función (¿te equivocaste con starmap()?). A continuación es ejemplo de trabajo con la matriz de pasar explícitamente:

    from multiprocessing import Pool
    
    def count_it( (arr, key) ):
        count = 0
        for c in arr:
            if c == key:
                count += 1
        return count
    
    if __name__ == '__main__':
        testData = "abcabcs bsdfsdf gdfg dffdgdfg sdfsdfsd sdfdsfsdf"
        pool = Pool()
        print pool.map(count_it, [(testData, key) for key in ["a", "b", "s", "d"]])
    • Que no es lo que él quiere, porque en teoría testData va a ser muy grande – y este método resulta ser en escabeche (necesidad de más memoria) y se copia a cada proceso (que requiere de al menos n x de almacenamiento original).
    • tienes razón, por eso he mencionado, tanto las formas posibles. Ejemplo de uso de una variable global que debería ser obvio, así que no es necesario en la lista.
    • sí, pero por desgracia, el método global no funciona en Windows se basa en la horquilla y unix copy-on-write. Si se utiliza el método global en windows multiprocesamiento se adoban los datos y enviarlo a cada niño subproceso – de nuevo que requiere mucha más memoria.

Dejar respuesta

Please enter your comment!
Please enter your name here