2010-12-26 7 views
6

Estoy procesando grandes cantidades de datos, almacenados en un diccionario, mediante multiprocesamiento. Básicamente, todo lo que hago es cargar algunas firmas, almacenarlas en un diccionario, crear un objeto dict compartido (obtener el objeto 'proxy' devuelto por Manager.dict()) y pasar este proxy como argumento a la función que tiene para ser ejecutado en multiprocesamiento.python: compartir diccionarios de gran tamaño con multiprocesamiento

Solo para aclarar:

signatures = dict() 
load_signatures(signatures) 
[...] 
manager = Manager() 
signaturesProxy = manager.dict(signatures) 
[...] 
result = pool.map (myfunction , [ signaturesProxy ]*NUM_CORES) 

Ahora, si todo funciona perfectamente firmas es inferior a 2 millones de entradas o menos. De todos modos, tengo que procesar un diccionario con 5.8M claves (decapado firmas en formato binario genera un archivo de 4.8 GB). En este caso, el proceso muere durante la creación del objeto proxy:

Traceback (most recent call last): 
    File "matrix.py", line 617, in <module> 
signaturesProxy = manager.dict(signatures) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 634, in temp 
token, exp = self._create(typeid, *args, **kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 534, in _create 
id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 79, in dispatch 
raise convert_to_error(kind, result) 
multiprocessing.managers.RemoteError: 
--------------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 173, in handle_request 
    request = c.recv() 
EOFError 
--------------------------------------------------------------------------- 

sé la estructura de datos es enorme, pero estoy trabajando en una máquina equipada w/32 GB de RAM, y corriendo arriba veo que el proceso, después de cargar las firmas, ocupa 7 GB de RAM. A continuación, comienza a construir el objeto proxy y el uso de la memoria RAM sube a ~ 17GB de RAM, pero nunca se acerca a 32. En este punto, el uso de RAM comienza a disminuir rápidamente y el proceso termina con el error anterior. Así que supongo que esto no se debe a un error de falta de memoria ...

¿Alguna idea o sugerencia?

Gracias,

Davide

Respuesta

-3

Si los diccionarios son de solo lectura, no necesita objetos proxy en la mayoría de los sistemas operativos.

Simplemente cargue los diccionarios antes de iniciar a los trabajadores, y colóquelos en algún lugar al que puedan comunicarse; el lugar más simple es globalmente para un módulo. Serán legibles por los trabajadores.

from multiprocessing import Pool 

buf = "" 

def f(x): 
    buf.find("x") 
    return 0 

if __name__ == '__main__': 
    buf = "a" * 1024 * 1024 * 1024 
    pool = Pool(processes=1) 
    result = pool.apply_async(f, [10]) 
    print result.get(timeout=5) 

Esto sólo se utiliza 1 GB de memoria combinada, no 1 GB para cada proceso, porque cualquier sistema operativo moderno hará que una sombra de copia por escritura de los datos creados antes de que el tenedor. Solo recuerde que los cambios en los datos no serán vistos por otros trabajadores, y la memoria, por supuesto, se asignará a los datos que modifique.

Utilizará algo de memoria: se modificará la página de cada objeto que contiene el recuento de referencias, por lo que se asignará. Si esto importa depende de los datos.

Esto funcionará en cualquier sistema operativo que implemente bifurcación ordinaria. No funcionará en Windows; su modelo de proceso (paralizado) requiere relanzar todo el proceso para cada trabajador, por lo que no es muy bueno para compartir datos.

+1

¿Funciona esto con Windows 7 (que definitivamente es un sistema operativo moderno?) –

+0

@Seun: No lo sé; prueba probarlo Dudo que su modelo de proceso sea más moderno que las versiones anteriores; Windows siempre ha estado en la edad oscura sobre eso. –

+1

(No hay nada como SO para votos indirectos aleatorios e incorrectos) –

2

Con el fin de ahorrar tiempo y no tener que depurar problemas a nivel de sistema, tal vez usted podría dividir su diccionario récord de 5,8 millones en tres conjuntos de ~ 2 millones cada uno, y ejecutar el trabajo 3 veces.

+0

que pude pero no es una solución óptima ya que, de todos modos, al final tendría que reconstruir todo el diccionario y usarlo para otras operaciones –

+0

Entonces Parece que su tarea sería apropiado para Hadoop/MapReduce ... Tal vez deberías verificarlo. – Fragsworth

6

¿Por qué no pruebas esto con una base de datos? Las bases de datos no están limitadas a RAM direccionables/físicas y son seguras para uso de multiproceso/proceso.

0

Creo que el problema que te encontraste fue que la tabla dict o hash se redimensionaba a medida que crecía. Inicialmente, el dict tiene un número determinado de cubos disponibles. No estoy seguro acerca de Python, pero sé que Perl comienza con 8 y luego cuando los cubos están llenos, el hash es recreado por 8 más (es decir.8, 16, 32, ...).

El contenedor es un lugar de aterrizaje para el algoritmo hash. Las 8 ranuras no significan 8 entradas, significa 8 ubicaciones de memoria. Cuando se agrega el nuevo elemento, se genera un hash para esa clave y luego se almacena en ese cubo.

Aquí es donde las colisiones entran en juego. Cuantos más elementos haya en un cubo, más lenta será la función, ya que los elementos se anexan secuencialmente debido al tamaño dinámico de la ranura.

Un problema que puede ocurrir es que sus claves son muy similares y producen el mismo resultado hash, lo que significa que la mayoría de las claves están en una ranura. La asignación previa de los depósitos hash ayudará a eliminar esto y, de hecho, mejorará el tiempo de procesamiento y la administración de claves, además de que ya no necesita hacer todo ese intercambio.

Sin embargo, creo que todavía está limitado a la cantidad de memoria contigua libre y eventualmente tendrá que ir a la solución de base de datos.

nota al margen: Todavía soy nuevo en Python, sé que en Perl puedes ver las estadísticas de hash haciendo print% HASHNAME, se mostrará la distribución del uso del cubo. Te ayuda a identificar los recuentos de colisiones, en caso de que necesites preasignar cubos. ¿Se puede hacer esto en Python también?

Rich

Cuestiones relacionadas