modo leer gestion ejercicios crear con como binarios binario archivos archivo apertura python performance file-io

leer - python con archivos



Mejora la velocidad de lectura y conversión de archivos binarios (5)

Sé que ha habido algunas preguntas con respecto a la lectura de archivos, el manejo de datos binarios y la conversión de enteros usando struct before, así que vengo aquí para preguntar sobre un código que tengo y que creo que se está tomando demasiado tiempo para ejecutarlo. El archivo que se lee es una grabación de muestra de datos multicanal (enteros cortos), con intervalos de datos intercalados (de ahí las declaraciones anidadas for ). El código es el siguiente:

# channel_content is a dictionary, channel_content[channel][''nsamples''] is a string for rec in xrange(number_of_intervals)): for channel in channel_names: channel_content[channel][''recording''].extend( [struct.unpack( "h", f.read(2))[0] for iteration in xrange(int(channel_content[channel][''nsamples'']))])

Con este código, obtengo 2.2 segundos por megabyte de lectura con un doble núcleo con 2Mb de RAM, y mis archivos generalmente tienen 20+ Mb, lo que produce una demora muy molesta (especialmente considerando otro programa shareware de referencia que estoy tratando de duplicar carga el archivo MUCHO más rápido).

Lo que me gustaría saber:

  1. Si hay alguna violación de las "buenas prácticas": bucles mal arreglados, operaciones repetitivas que llevan más tiempo de lo necesario, uso de tipos de contenedores ineficientes (¿diccionarios?), Etc.
  2. Si esta velocidad de lectura es normal, o normal a Python, y si la velocidad de lectura
  3. Si la creación de una extensión compilada en C ++ podría mejorar el rendimiento, y si sería un enfoque recomendado.
  4. (por supuesto) Si alguien sugiere alguna modificación a este código, preferiblemente basado en experiencia previa con operaciones similares.

Gracias por leer

(Ya he publicado algunas preguntas sobre este trabajo mío, espero que no tengan ninguna relación conceptual, y también espero no ser demasiado repetitivo).

Editar: channel_names es una lista, así que hice la corrección sugerida por @eumiro (elimine los paréntesis de tipografía)

Editar: actualmente voy con la sugerencia de Sebastian de usar array con el método fromfile() , y pronto pondré aquí el código final. Además, cada contribución me ha sido muy útil, y agradezco mucho a todos los que amablemente respondieron.

Formulario final después de ir con array.fromfile() una vez, y luego extender alternativamente una matriz para cada canal cortando la matriz grande:

fullsamples = array(''h'') fullsamples.fromfile(f, os.path.getsize(f.filename)/fullsamples.itemsize - f.tell()) position = 0 for rec in xrange(int(self.header[''nrecs''])): for channel in self.channel_labels: samples = int(self.channel_content[channel][''nsamples'']) self.channel_content[channel][''recording''].extend( fullsamples[position:position+samples]) position += samples

La mejora de velocidad fue muy impresionante al leer el archivo un poco a la vez, o al usar struct en cualquier forma.


Si los archivos son solo 20-30M, ¿por qué no leer todo el archivo, decodificar los nums en una sola llamada para unpack y luego distribuirlos entre los canales iterando sobre la matriz:

data = open(''data.bin'', ''rb'').read() values = struct.unpack(''%dh'' % len(data)/2, data) del data # iterate over channels, and assign from values using indices/slices

Una prueba rápida mostró que esto dio como resultado una aceleración de 10x sobre struct.unpack(''h'', f.read(2)) en un archivo de 20M.


extend () acepta iterables, es decir, en lugar de .extend([...]) , puede escribir .extend(...) . Es probable que acelere el programa porque extender () se procesará en un generador, no más en una lista construida

Hay una incoherencia en su código: define first channel_content = {} , y luego realiza channel_content[channel][''recording''].extend(...) que necesita la existencia preliminar de un canal clave y una subclave '' grabar '' con una lista como un valor para poder extender algo

¿Cuál es la naturaleza de self.channel_content[channel][''nsamples''] para que pueda enviarse a la función int () ?

¿De dónde viene number_of_intervals ? ¿Cuál es la naturaleza de los intervalos?

En el rec in xrange(number_of_intervals)): loop, ya no veo rec . Así que me parece que estás repitiendo el mismo proceso de bucle for channel in channel_names: tantas veces como el número expresado por number_of_intervals . ¿Hay valores de number_of_intervals * int (self.channel_content [channel] [''nsamples'']) * 2 para leer en f?

Leí en el documento:

clase struct.Struct (formato)

Devuelve un nuevo objeto Struct que escribe y lee datos binarios de acuerdo con el formato de cadena de formato. Crear un objeto Struct una vez y llamar a sus métodos es más eficiente que llamar a las funciones struct con el mismo formato, ya que la cadena de formato solo necesita compilarse una vez.

Esto expresa la misma idea que samplebias.

Si su objetivo es crear un diccionario, también existe la posibilidad de usar dict () con un generador como argumento

.

EDITAR

Propongo

channel_content = {} for rec in xrange(number_of_intervals)): for channel in channel_names: N = int(self.channel_content[channel][''nsamples'']) upk = str(N)+"h", f.read(2*N) channel_content[channel][''recording''].extend(struct.unpack(x) for i,x in enumerate(upk) if not i%2)

No sé cómo tener en cuenta la sugerencia de JF Sebastian de usar array


No estoy seguro si sería más rápido, pero trataría de decodificar trozos de palabras en lugar de una palabra por vez. Por ejemplo, podría leer 100 bytes de datos en un momento como:

s = f.read(100) struct.unpack(str(len(s)/2)+"h", s)


Una sola matriz de la llamada del archivo es definitivamente más rápida, pero no funcionará si las bases de datos están intercaladas con otros tipos de valores.

En tales casos, otro gran aumento de velocidad que se puede combinar con las respuestas estructurales anteriores, es que en lugar de llamar a la función desempaquetar varias veces, precompile un objeto struct.Struct con el formato de cada fragmento. De los documentos :

Crear un objeto Struct una vez y llamar a sus métodos es más eficiente que llamar a las funciones struct con el mismo formato, ya que la cadena de formato solo necesita compilarse una vez.

Entonces, por ejemplo, si quiere desempaquetar 1000 pantalones cortos y carrozas intercalados a la vez, puede escribir:

chunksize = 1000 structobj = struct.Struct("hf" * chunksize) while True: chunkdata = structobj.unpack(fileobj.read(structobj.size))

(Tenga en cuenta que el ejemplo es solo parcial y necesita tener en cuenta el cambio del tamaño de fragmento al final del archivo y romper el ciclo while.)


Puede usar una array para leer sus datos:

import array import os fn = ''data.bin'' a = array.array(''h'') a.fromfile(open(fn, ''rb''), os.path.getsize(fn) // a.itemsize)

Es 40 veces más rápido que struct.unpack de la respuesta de @ samplebias .