2011-05-20 17 views
5

Intentando cargar un archivo en python. Es un archivo muy grande (1.5Gb), pero tengo la memoria disponible y solo quiero hacer esto una vez (de ahí el uso de Python, solo necesito ordenar el archivo una vez para que Python sea una opción fácil).¿Por qué cargar este archivo requiere tanta memoria?

Mi problema es que al cargar este archivo está dando lugar a manera a mucho uso de memoria. Cuando cargué aproximadamente el 10% de las líneas en la memoria, Python ya está utilizando 700Mb, lo que es claramente demasiado. Alrededor del 50% del script se cuelga, usando 3.03 Gb de memoria real (y aumentando lentamente).

Sé que este no es el método más eficiente para ordenar un archivo (en cuanto a la memoria) pero solo quiero que funcione para poder pasar a problemas más importantes: D Entonces, ¿qué hay de malo con la siguiente pitón código que está causando el uso de la memoria masiva:

print 'Loading file into memory' 
input_file = open(input_file_name, 'r') 
input_file.readline() # Toss out the header 
lines = [] 
totalLines = 31164015.0 
currentLine = 0.0 
printEvery100000 = 0 
for line in input_file: 
    currentLine += 1.0 
    lined = line.split('\t') 
    printEvery100000 += 1 
    if printEvery100000 == 100000: 
     print str(currentLine/totalLines) 
     printEvery100000 = 0; 
    lines.append((lined[timestamp_pos].strip(), lined[personID_pos].strip(), lined[x_pos].strip(), lined[y_pos].strip())) 
input_file.close() 
print 'Done loading file into memory' 

EDIT: En caso de que alguien no está seguro, el consenso general parece ser que cada variable asignada se come más y más memoria. Lo "arreglé" en este caso 1) llamando a readLines(), que todavía carga todos los datos, pero solo tiene una sobrecarga variable de 'cadena' para cada línea. Esto carga todo el archivo usando aproximadamente 1.7 Gb. Luego, cuando llamo a lines.sort(), paso una función a la tecla que se divide en pestañas y devuelve el valor de la columna a la derecha, convertido a un int. Esto es lento en términos computacionales, y requiere mucha memoria en general, pero funciona. Aprendí un montón sobre overhad de asignación variable hoy: D

+0

Imagino porque las listas ocupan más espacio en la memoria que la suma de sus partes. –

+0

Es suficiente, pero estamos hablando ~ 5 veces más memoria de la que espero que se consuma. ¡No creo que tomen mucho más! – Hamy

+0

@Hamy Sí, parece demasiado para mí también. –

Respuesta

3

Aquí hay una estimación aproximada de la memoria necesaria, basada en las constantes derivadas de su ejemplo. Como mínimo, debe calcular la sobrecarga del objeto interno de Python para cada línea dividida, más la sobrecarga de cada cadena.

Se estima 9.1 GB para almacenar el archivo en la memoria, asumiendo las siguientes constantes, que son fuera por un poco, ya que sólo está utilizando parte de cada línea:

  • 1,5 GB de tamaño de archivo
  • 31,164,015 líneas totales
  • cada línea de división en una lista con 4 piezas

Código:

import sys 
def sizeof(lst): 
    return sys.getsizeof(lst) + sum(sys.getsizeof(v) for v in lst) 

GIG = 1024**3 
file_size = 1.5 * GIG 
lines = 31164015 
num_cols = 4 
avg_line_len = int(file_size/float(lines)) 

val = 'a' * (avg_line_len/num_cols) 
lst = [val] * num_cols 

line_size = sizeof(lst) 
print 'avg line size: %d bytes' % line_size 
print 'approx. memory needed: %.1f GB' % ((line_size * lines)/float(GIG)) 

Devuelve:

avg line size: 312 bytes 
approx. memory needed: 9.1 GB 
+0

Muy interesante, aunque estoy bastante seguro de que estas matemáticas no son correctas, a pesar de que la respuesta final (y el punto) que se requiere mucho más espacio es de hecho correcto. La razón por la que estoy dudando es que puedo llamar a readlines() y carga todo el archivo, usando alrededor de 1.5Gb, que parece ser el modelo de memoria que está buscando (basado en la definición de sizeof), p. un grupo de objetos de línea (Cadenas) que contienen cada uno un número de caracteres – Hamy

+0

Editar: toma alrededor de 1.7Gb para cargar usando readLines(), por lo que parece que el culpable es la asignación de variable – Hamy

+0

El estimado de 9.1GB es un nivel alto marque asumiendo que usa el 100% de cada línea (está dividiendo e indexando campos individuales, por lo que no conozco las longitudes de campo resultantes). Por lo tanto, si usa solo 2/3 de cada línea en promedio, obtendrá como resultado ~ 6GB, que se alinea con su 3GB al 50% de observación. – samplebias

1

No sé sobre el análisis del uso de la memoria, pero se podría intentar esto para conseguir que funcione sin el funcionamiento de la memoria. Ordenará en un nuevo archivo al que se accede utilizando una asignación de memoria (me han hecho creer que esto funcionará de manera eficiente [en términos de memoria]). Mmap tiene algunos funcionamientos específicos del SO, lo probé en Linux (escala muy pequeña).

Este es el código básico, para hacerlo funcionar con una eficiencia de tiempo decente, es probable que desee hacer una búsqueda binaria en el archivo ordenado para encontrar dónde insertar la línea, de lo contrario, probablemente llevará mucho tiempo.

Puede encontrar un algoritmo de búsqueda binaria de búsqueda de archivos en this question.

Con suerte una manera eficiente de la memoria de clasificar un archivo masivo de línea:

import os 
from mmap import mmap 

input_file = open('unsorted.txt', 'r') 
output_file = open('sorted.txt', 'w+') 

# need to provide something in order to be able to mmap the file 
# so we'll just copy the first line over 
output_file.write(input_file.readline()) 
output_file.flush() 
mm = mmap(output_file.fileno(), os.stat(output_file.name).st_size) 
cur_size = mm.size() 

for line in input_file: 
    mm.seek(0) 
    tup = line.split("\t") 
    while True: 
    cur_loc = mm.tell() 
    o_line = mm.readline() 
    o_tup = o_line.split("\t") 
    if o_line == '' or tup[0] < o_tup[0]: # EOF or we found our spot 
     mm.resize(cur_size + len(line)) 
     mm[cur_loc+len(line):] = mm[cur_loc:cur_size] 
     mm[cur_loc:cur_loc+len(line)] = line 
     cur_size += len(line) 
     break 
Cuestiones relacionadas