recorrer - Uso de memoria Python? cargar diccionarios grandes en la memoria
diccionarios python (4)
Eche un vistazo (Python 2.6, versión de 32 bits) ...:
>>> sys.getsizeof(''word,1'')
30
>>> sys.getsizeof((''word'', ''1''))
36
>>> sys.getsizeof(dict(word=''1''))
140
La cadena (tomar 6 bytes en el disco, claramente) obtiene una sobrecarga de 24 bytes (sin importar cuánto tiempo sea, agregue 24 a su longitud para encontrar cuánta memoria se necesita). Cuando lo divide en una tupla, eso es un poco más. Pero el dict
es lo que realmente hace explotar las cosas: incluso un dict vacío toma 140 bytes, una sobrecarga pura de mantener una toma de búsqueda basada en hash increíblemente rápida. Para ser rápido, una tabla hash debe tener baja densidad, y Python garantiza que una dict
sea siempre de baja densidad (al tomar mucha memoria extra para ella).
La forma más eficiente de almacenar pares de clave / valor es una lista de tuplas, pero la búsqueda por supuesto será muy lenta (incluso si clasificas la lista y usas bisect
para la búsqueda, todavía será más lenta que una dict).
Considere el uso de shelve lugar, que utilizará poca memoria (ya que los datos residen en el disco) y aún ofrecerá un rendimiento de búsqueda bastante intenso (no tan rápido como un dict en memoria, por supuesto, pero para una gran cantidad de datos será mucho más rápido que la búsqueda en una lista de tuplas, ¡incluso una ordenada, puede ser! -).
Hola a todos, tengo un archivo en el disco que solo tiene 168 MB. Es solo una lista de palabras separadas por comas, id. La palabra puede tener entre 1 y 5 palabras. Hay 6.5 millones de líneas. Creé un diccionario en python para cargar esto en la memoria, de modo que pueda buscar texto entrante contra esa lista de palabras. Cuando python lo carga en la memoria, muestra 1.3 GB de espacio RAM utilizado. ¿Alguna idea de por qué es eso?
así que digamos que mi archivo de palabras se ve así ...
1,word1
2,word2
3,word3
luego agregue 6.5 millones a eso, luego recorro ese archivo y creo un diccionario (Python 2.6.1)
def load_term_cache():
"""will load the term cache from our cached file instead of hitting mysql. If it didn''t
preload into memory it would be 20+ million queries per process"""
global cached_terms
dumpfile = os.path.join(os.getenv("MY_PATH"), ''datafiles'', ''baseterms.txt'')
f = open(dumpfile)
cache = csv.reader(f)
for term_id, term in cache:
cached_terms[term] = term_id
f.close()
Solo hacer eso explota la memoria. Veo el monitor de actividad y clava la memoria a todos los disponibles hasta alrededor de 1.5GB de RAM. En mi computadora portátil solo comienza a intercambiarse. ¿Alguna idea de cómo almacenar de manera más eficiente los pares clave / valor en la memoria con python?
Gracias
ACTUALIZACIÓN: Intenté usar el módulo anydb y después de 4.4 millones de registros, simplemente muere el número de punto flotante es el número de segundos transcurridos desde que intenté cargarlo
56.95
3400018
60.12
3600019
63.27
3800020
66.43
4000021
69.59
4200022
72.75
4400023
83.42
4600024
168.61
4800025
338.57
puedes ver que estaba funcionando genial. 200,000 filas cada pocos segundos insertadas hasta que golpeé una pared y el tiempo se duplicó.
import anydbm
i=0
mark=0
starttime = time.time()
dbfile = os.path.join(os.getenv("MY_PATH"), ''datafiles'', ''baseterms'')
db = anydbm.open(dbfile, ''c'')
#load from existing baseterm file
termfile = os.path.join(os.getenv("MY_PATH"), ''datafiles'', ''baseterms.txt.LARGE'')
for line in open(termfile):
i += 1
pieces = line.split('','')
db[str(pieces[1])] = str(pieces[0])
if i > mark:
print i
print round(time.time() - starttime, 2)
mark = i + 200000
db.close()
Muchas ideas. Sin embargo, si desea ayuda práctica, edite su pregunta para mostrar TODO su código. Díganos también qué es "eso" que muestra la memoria utilizada, qué muestra cuando carga un archivo con cero entradas, y en qué plataforma se encuentra, y qué versión de Python.
Usted dice que "la palabra puede tener entre 1 y 5 palabras". ¿Cuál es la longitud promedio del campo clave en BYTES? ¿Los identificadores son enteros? Si es así, ¿cuál es el número entero mínimo y máximo? Si no, ¿cuál es la longitud promedio si ID en bytes? Para habilitar la comprobación cruzada de todo lo anterior, ¿cuántos bytes hay en su archivo de 6.5M de línea?
En cuanto a su código, una word1,1
archivo de 1 word1,1
creará un dict d[''1''] = ''word1''
... ¿no es eso bassackwards?
Actualización 3: Más preguntas: ¿Cómo se codifica la "palabra"? ¿Estás seguro de que no llevas una carga de espacios al final en ninguno de los dos campos?
Actualización 4 ... Usted preguntó " cómo almacenar de manera más eficiente los pares clave / valor en la memoria con python " y nadie respondió con precisión .
Tienes un archivo de 168 Mb con 6.5 millones de líneas. Eso es 168 * 1.024 ** 2 / 6.5 = 27.1 bytes por línea. Elimina 1 byte para la coma y 1 byte para la nueva línea (suponiendo que sea una plataforma * x) y nos quedan 25 bytes por línea. Suponiendo que el "id" está destinado a ser único, y como parece ser un número entero, supongamos que el "id" tiene 7 bytes de longitud; eso nos deja un tamaño promedio de 18 bytes para la "palabra". ¿Eso coincide con tus expectativas?
Por lo tanto, queremos almacenar una clave de 18 bytes y un valor de 7 bytes en una tabla de búsqueda en la memoria.
Supongamos una plataforma CPython 2.6 de 32 bits.
>>> K = sys.getsizeof(''123456789012345678'')
>>> V = sys.getsizeof(''1234567'')
>>> K, V
(42, 31)
Tenga en cuenta que sys.getsizeof(str_object) => 24 + len(str_object)
Las tuplas fueron mencionadas por un respondedor. Observe cuidadosamente lo siguiente:
>>> sys.getsizeof(())
28
>>> sys.getsizeof((1,))
32
>>> sys.getsizeof((1,2))
36
>>> sys.getsizeof((1,2,3))
40
>>> sys.getsizeof(("foo", "bar"))
36
>>> sys.getsizeof(("fooooooooooooooooooooooo", "bar"))
36
>>>
Conclusión: sys.getsizeof(tuple_object) => 28 + 4 * len(tuple_object)
... solo permite un puntero a cada elemento, no permite los tamaños de los elementos.
Un análisis similar de listas muestra que sys.getsizeof(list_object) => 36 + 4 * len(list_object)
... otra vez es necesario agregar los tamaños de los elementos. Hay una consideración adicional: CPython sobreasigna listas para que no tenga que llamar al sistema realloc () en cada llamada list.append (). Para un tamaño suficientemente grande (¡como 6,5 millones!), La sobreasignación es del 12,5 por ciento; consulte la fuente (Objects / listobject.c). Esta sobreasignación no se realiza con tuplas (su tamaño no cambia).
Aquí están los costos de varias alternativas a dict para una tabla de búsqueda basada en la memoria:
Lista de tuplas:
Cada tupla tendrá 36 bytes para la 2-tupla misma, más K y V para los contenidos. Entonces, N de ellos tomará N * (36 + K + V); entonces necesitas una lista para mantenerlos, por lo que necesitamos 36 + 1.125 * 4 * N para eso.
Total para la lista de tuplas: 36 + N * (40.5 + K + v)
Eso es 26 + 113.5 * N ( aproximadamente 709 MB cuando es 6.5 millones)
Dos listas paralelas:
(36 + 1.125 * 4 * N + K * N) + (36 + 1.125 * 4 * N + V * N) es decir, 72 + N * (9 + K + V)
Tenga en cuenta que la diferencia entre 40.5 * N y 9 * N es de aproximadamente 200 MB cuando N es de 6,5 millones.
Valor almacenado como int no str:
Pero eso no es todo. Si los ID son en realidad enteros, podemos almacenarlos como tales.
>>> sys.getsizeof(1234567)
12
Eso es 12 bytes en lugar de 31 bytes para cada objeto de valor. Esa diferencia de 19 * N es un ahorro adicional de aproximadamente 118 MB cuando N es de 6,5 millones.
Utilice array.array (''l'') en lugar de list para el valor (entero):
Podemos almacenar esos enteros de 7 dígitos en un array.array (''l''). No hay objetos int, ni punteros a ellos, solo un valor entero con signo de 4 bytes. Bonificación: las matrices están sobreasignadas por solo 6.25% (para N grande). Así que eso es 1.0625 * 4 * N en lugar del anterior (1.125 * 4 + 12) * N, un ahorro adicional de 12.25 * N, es decir 76 MB.
Así que estamos en 709 - 200 - 118 - 76 = alrededor de 315 MB .
NB Errores y omisiones exceptuados - es 0127 en mi TZ :-(
Tengo el mismo problema aunque sea más tarde. Los demás han respondido bien a esta pregunta. Y ofrezco un fácil de usar (tal vez no tan fácil :-)) y una alternativa bastante eficiente, eso es pandas.DataFrame
. Funciona bien en el uso de memoria al guardar datos grandes.
convierta sus datos en un dbm (importe anydbm, o use berkerley db mediante import bsddb ...), y luego use dbm API para acceder a él.
la razón para explotar es que python tiene metainformación adicional para cualquier objeto, y el dict necesita construir una tabla hash (que requeriría más memoria). acabas de crear tantos objetos (6.5M) para que los metadatos se vuelvan demasiado grandes.
import bsddb
a = bsddb.btopen(''a.bdb'') # you can also try bsddb.hashopen
for x in xrange(10500) :
a[''word%d'' %x] = ''%d'' %x
a.close()
Este código tarda solo 1 segundo en ejecutarse, por lo que creo que la velocidad es correcta (ya que dijo 10500 líneas por segundo). btopen crea un archivo db con 499,712 bytes de longitud y hashopen crea 319,488 bytes.
Con la entrada de xrange como 6.5M y usando btopen, obtuve 417,080 KB en el tamaño del archivo de salida y alrededor de 1 o 2 minutos para completar la inserción. Así que creo que es totalmente adecuado para ti.