2011-03-31 5 views
8

para darle contexto:¿Lectura de múltiples datos en escabeche de Python a la vez, almacenamiento en búfer y nuevas líneas?

Tengo un gran archivo f, varios conciertos en el tamaño. Contiene pepinillos consecutivos de diferentes objetos que se generaron mediante la ejecución de

for obj in objs: cPickle.dump(obj, f)

quiero tomar ventaja de búfer cuando se lee este archivo. Lo que quiero es leer varios objetos recogidos en un búfer a la vez. Cual es la mejor manera de hacer esto? Quiero un análogo de para datos en escabeche. De hecho, si los datos recogidos son de hecho delimitados por una línea nueva, uno podría usar las líneas de lectura, pero no estoy seguro de si eso es cierto.

Otra opción que tengo en mente es dumps() primero el objeto escabechado en una cadena y luego escribir las cadenas en un archivo, cada uno separado por una nueva línea. Para volver a leer el archivo, puedo usar readlines() y loads(). Pero me temo que un objeto escabechado puede tener el carácter "\n" y arrojará este esquema de lectura de archivos. ¿Es mi miedo infundado?

Una opción es extraerla como una gran lista de objetos, pero eso requerirá más memoria de la que puedo permitir. La configuración se puede acelerar mediante multi-threading, pero no quiero ir allí antes de que el buffer funcione correctamente. ¿Cuál es la "mejor práctica" para situaciones como esta?

EDIT: También puedo leer en bytes sin formato en un búfer e invocar cargas sobre eso, pero necesito saber cuántos bytes de ese búfer consumieron las cargas para que pueda tirar la cabeza.

+1

No debería estar ya amortiguar sucediendo detrás de las escenas. También los hilos no ayudarán. –

+0

Si tiene control sobre la creación del archivo pickle, probablemente podría hacerlo mucho más pequeño usando 'pickle.dump (obj, f, pickle.HIGHEST_PROTOCOL)' (o el equivalente 'pickle.dump (obj, f, -1) ') que es un protocolo binario y mucho más compacto que el ASCII predeterminado que está recibiendo. Tener un archivo mucho más pequeño podría mitigar las preocupaciones sobre el almacenamiento en búfer. De hecho, probablemente significaría que "buscar líneas que terminen en truco' '. \ N'' "en la [respuesta] de @Kirk Strauser (http://stackoverflow.com/a/5507750/355230) no funcionaría. – martineau

Respuesta

5

file.readlines() devuelve una lista de todo el contenido del archivo. Querrá leer algunas líneas a la vez. Yo creo este código debe ingenua unpickle sus datos:

import pickle 
infile = open('/tmp/pickle', 'rb') 
buf = [] 
while True: 
    line = infile.readline() 
    if not line: 
     break 
    buf.append(line) 
    if line.endswith('.\n'): 
     print 'Decoding', buf 
     print pickle.loads(''.join(buf)) 
     buf = [] 

Si usted tiene ningún control sobre el programa que genera los pepinillos, me quedaría con uno de:

  1. Uso del módulo shelve.
  2. Imprima la longitud (en bytes) de cada salmuera antes de escribirla en el archivo para que sepa exactamente cuántos bytes leer cada vez.
  3. Igual que el anterior, pero escriba la lista de enteros en un archivo separado para que pueda usar esos valores como un índice en el archivo que contiene los encurtidos.
  4. Elija una lista de K objetos a la vez. Escriba la longitud de esa salmuera en bytes. Escribe el pepinillo. Repetir.

Por cierto, sospecho que los file 's built-in de amortiguación debe conseguir que el 99% del rendimiento de las ganancias que usted está buscando.

Si está convencido de que las E/S lo están bloqueando, ¿ha pensado en probar mmap() y dejar que el SO maneje el embalaje en bloques a la vez?

#!/usr/bin/env python 

import mmap 
import cPickle 

fname = '/tmp/pickle' 
infile = open(fname, 'rb') 
m = mmap.mmap(infile.fileno(), 0, access=mmap.ACCESS_READ) 
start = 0 
while True: 
    end = m.find('.\n', start + 1) + 2 
    if end == 1: 
     break 
    print cPickle.loads(m[start:end]) 
    start = end 
+0

Esto es muy similar a la solución que tuve, excepto que consideré el uso de lectinas con un límite preespecificado en el número de bytes para leer. ¿Tengo garantizado que las salmueras nunca tendrán una nueva línea adentro? Ese es el aspecto crítico que quiero saber, de lo contrario, la nuestra es una solución con errores. ¿Te leí correctamente que los pepinillos individuales terminan con ". \ N", en otras palabras, un punto seguido de una nueva línea? Gracias por las otras sugerencias. En este momento sigo invocando cPickle.load() en un objeto de archivo en búfer, y puedo leer unos 200 objetos/seg cada uno alrededor de 800 bytes en avg, que aún es lento. – san

+0

No soy un experto, pero mirando una tabla de base de datos que tengo con algunos miles de escarabajos en ella, todas terminan con '. \ N'. Mira 'pickle.dumps ('\ n')'; parece escapar '\ n', aunque no sé si eso está garantizado. ¿Qué opinas sobre el n. ° 4, donde recoges muchos elementos de una lista y luego los descomprimes de un único archivo.read()? –

+0

Sí # 4 es bueno. El único inconveniente es que el consumidor estará limitado a ese "buffer" fijo. Pero si lo que dices sobre ". \ N" es correcto, el problema ya está resuelto :) – san

2

Es posible que desee mirar el módulo shelve. Utiliza un módulo de base de datos como dbm para crear un diccionario de objetos en el disco. Los objetos mismos todavía están serializados usando pickle. De esta forma, podría leer conjuntos de objetos en lugar de un gran pepinillo a la vez.

+0

Gracias por su respuesta. Sin embargo, no puedo ver cómo esto ayudará en un almacenamiento en búfer más eficiente. Lo que estoy buscando es una forma de leer en un gran trozo de bytes y eliminar algunos objetos 'K' a la vez. La idea es aliviar el cuello de botella de E/S. Creo que lo que estás sugiriendo es que intento cargar una cantidad de objetos suministrando muchas claves. El módulo busca los valores correspondientes a esas teclas. No es mucho trabajo extra, y por lo tanto un paso atrás, dado que no me importa el orden de los objetos, solo que quiero o revisarlos todos. Pero podría haberte equivocado. – san

2

Si desea agregar búfer a cualquier archivo, abrirlo a través io.open(). Aquí hay un ejemplo que leerá de la secuencia subyacente en trozos de 128K. Cada llamada a cPickle.load() se cumplirá desde el buffer interno hasta que se agote, y luego otro trozo será leído desde el archivo subyacente:

import cPickle 
import io 

buf = io.open('objects.pkl', 'rb', buffering=(128 * 1024)) 
obj = cPickle.load(buf) 
+0

Eso es exactamente lo que hago, de hecho lo leí en un archivo gzip comprimido (nivel de compresión 2) usando una interfaz de almacenamiento intermedio, pero esperaba agarrar tantos objetos en escabeche como pueda, de manera eficiente. Así que la respuesta que recibo es que sin enredar esto es principalmente lo rápido que esto será. – san

6

Usted no necesita hacer nada, creo.

>>> import pickle 
>>> import StringIO 
>>> s = StringIO.StringIO(pickle.dumps('apples') + pickle.dumps('bananas')) 
>>> pickle.load(s) 
'apples' 
>>> pickle.load(s) 
'bananas' 
>>> pickle.load(s) 

Traceback (most recent call last): 
    File "<pyshell#25>", line 1, in <module> 
    pickle.load(s) 
    File "C:\Python26\lib\pickle.py", line 1370, in load 
    return Unpickler(file).load() 
    File "C:\Python26\lib\pickle.py", line 858, in load 
    dispatch[key](self) 
    File "C:\Python26\lib\pickle.py", line 880, in load_eof 
    raise EOFError 
EOFError 
>>> 
Cuestiones relacionadas