2011-02-10 18 views
21

He creado un perfil de algún código heredado que he heredado con cProfile. Hubo un montón de cambios que ya he hecho que me han ayudado (¡como usar las extensiones C de simplejson!).Aceleración de la escritura en los archivos

Básicamente este script está exportando datos de un sistema a un archivo de ancho fijo ASCII. Cada fila es un registro y tiene muchos valores. Cada línea tiene 7158 caracteres y contiene una tonelada de espacios. El recuento total de registros es de 1,5 millones de registros. Cada fila se genera de a una por vez y toma un tiempo (5-10 filas por segundo).

Al generarse cada fila, se escribe en el disco lo más simple posible. El perfilado indica que aproximadamente 19-20% del tiempo total se gasta en file.write(). Para un caso de prueba de 1,500 filas, eso es 20 segundos. Me gustaría reducir ese número.

Ahora parece que la próxima victoria reducirá la cantidad de tiempo dedicado a escribir en el disco. Me gustaría reducirlo, si es posible. Puedo mantener un caché de registros en la memoria, pero no puedo esperar hasta el final y volcarlo todo a la vez.

fd = open(data_file, 'w') 
for c, (recordid, values) in enumerate(generatevalues()): 
     row = prep_row(recordid, values) 
     fd.write(row) 
     if c % 117 == 0: 
       if limit > 0 and c >= limit: 
         break 
       sys.stdout.write('\r%s @ %s' % (str(c + 1).rjust(7), datetime.now())) 
       sys.stdout.flush() 

Lo primero que pensé fue mantener un caché de registros en una lista y escribirlos en lotes. ¿Sería eso más rápido? Algo como:

rows = [] 
for c, (recordid, values) in enumerate(generatevalues()): 
     rows.append(prep_row(recordid, values)) 
     if c % 117 == 0: 
      fd.write('\n'.join(rows)) 
      rows = [] 

Mi segundo pensamiento sería usar otro hilo, pero eso me da ganas de morir dentro.

+0

¿Cuál es el cuello de botella de la aplicación? –

+2

Pensé que era claro. Pasar el 20% de su tiempo escribiendo en el disco una fila a la vez. – chmullig

+0

Bueno, haz el cambio y perfilalo? Se espera que tenga poco efecto, ya que la E/S de archivo suele estar almacenada en línea ... o eso supongo. – delnan

Respuesta

19

Encuadrar las escrituras en grupos de 500 aceleró significativamente las escrituras. Para este caso de prueba, las filas de escritura individualmente tomaron 21.051 segundos en E/S, mientras que escribir en lotes de 117 tomó 5.685 segundos para escribir el mismo número de filas. Los lotes de 500 tomaron un total de solo 0.266 segundos.

+3

y ahora puede agregar subprocesos para la escritura de archivos para hacer asynhronus escribiendo – ted

+0

Mejor aún: solo imprime en stdout, transfiéralo a un archivo y permita que el sistema operativo realice el almacenamiento en búfer y la escritura por lotes para usted. :) –

+0

@LesterCheung, ¿podría dar más detalles? – blindguy

32

En realidad, su problema no es que file.write() tome el 20% de su tiempo. ¡Es el 80% del tiempo que no estás en file.write()!

Escribir en el disco es lento. Realmente no hay nada que puedas hacer al respecto. Simplemente lleva una gran cantidad de tiempo escribir cosas en el disco. No hay casi nada que puedas hacer para acelerarlo.

Lo que desea es que el tiempo de E/S sea la parte más grande del programa, de modo que su velocidad esté limitada por la velocidad del disco duro, no por su tiempo de procesamiento. ¡Lo ideal es que file.write() tenga un 100% de uso!

+0

+1, y normalmente estoy de acuerdo con usted. Sin embargo, en este caso, el proceso externo que estoy utilizando para generar los datos es super lento, y quiero minimizar todo lo demás tanto como sea posible. Supongo que debería hablar en segundos y concentrarme en reducir eso. Editará para aclarar # de segundos. – chmullig

+0

Se actualizó para especificar que se tardan 20 segundos en escribir 1,500 registros. Realmente me importa ese número, solo estaba usando los porcentajes para demostrar que realmente vale la pena optimizarlo. – chmullig

+0

@chmullig, ¿qué% se está gastando esperando el proceso externo? –

1

Puede hacer mmap en python, que podría ayuda. Pero sospecho que cometió un error al perfilar, porque 7k * 1500 en 20 segundos es de aproximadamente 0.5 Mbytes/s. Haga una prueba en la que escriba líneas al azar con la misma longitud, y verá que es mucho más rápido que eso.

Cuestiones relacionadas