2010-04-14 20 views
8

Utilizo el siguiente código para registrar un mapa, es rápido cuando solo contiene ceros, pero tan pronto como hay datos reales en el mapa se vuelve insoportablemente lento ... ¿Hay alguna manera de hacer esto más rápido?Formato de cadena de Python demasiado lento

log_file = open('testfile', 'w') 
for i, x in ((i, start + i * interval) for i in range(length)): 
    log_file.write('%-5d %8.3f %13g %13g %13g %13g %13g %13g\n' % (i, x, 
     map[0][i], map[1][i], map[2][i], map[3][i], map[4][i], map[5][i])) 
+0

¿Qué tamaño tiene el conjunto de datos en el mapa? Puede ser más fácil comprimir (mapear [0], mapear [1], etc ...), luego recorrer las tuplas resultantes. –

+0

@Josh No creo que ese sea el problema, como se dijo, es rápido cuando es todo ceros (como menos de un segundo), pero insoportablemente lento con contenido real (como dos minutos para un mapa de 600 de longitud). – wich

+1

¿está seguro de que se debe al formato de cadena y no a escribir? – Francesco

Respuesta

3

le sugiero que ejecuta su código usando el módulo cProfile y post-procesamiento de los resultados según lo indicado en http://docs.python.org/library/profile.html. Esto le permitirá saber exactamente cuánto tiempo se gasta en la llamada al str.__mod__ para el formato de cadena y cuánto se gasta haciendo otras cosas, como escribir el archivo y hacer las búsquedas __getitem__ para map[0][i] y tal.

2

Primero he comprobado% contra backquoting. % es más rápido. Compré% (tuple) contra 'string'.format(). Un error inicial me hizo pensar que era más rápido. Pero no. % es más rápido.

Por lo tanto, ya está haciendo su gran cantidad de conversiones de flotador a cadena de la manera más rápida que puede hacerlo en Python.

El código de demostración a continuación es feo código de demostración. Por favor, no me hablen sobre xrange versus rango u otra pedantería. KThxBye.

Mi prueba ad-hoc y altamente no científica indica que (a)% (1.234,) operaciones en Python 2.5 en Linux es más rápido que% (1.234, ...) operación Python 2.6 en Linux, para el código de prueba a continuación , con la condición de que el intento de usar 'string'.format() no funcione en las versiones de Python anteriores a 2.6. Y así.

# this code should never be used in production. 
# should work on linux and windows now. 

import random 
import timeit 
import os 
import tempfile 


start = 0 
interval = 0.1 

amap = [] # list of lists 
tmap = [] # list of tuples 

def r(): 
    return random.random()*500 

for i in xrange(0,10000): 
     amap.append ([r(),r(),r(),r(),r(),r()]) 

for i in xrange(0,10000): 
     tmap.append ((r(),r(),r(),r(),r(),r())) 




def testme_percent(): 
    log_file = tempfile.TemporaryFile() 
    try: 
     for qmap in amap: 
      s = '%g %g %g %g %g %g \n' % (qmap[0], qmap[1], qmap[2], qmap[3], qmap[4], qmap[5]) 
      log_file.write(s) 
    finally: 
     log_file.close(); 

def testme_tuple_percent(): 
    log_file = tempfile.TemporaryFile() 
    try:  
     for qtup in tmap: 
      s = '%g %g %g %g %g %g \n' % qtup 
      log_file.write(s); 
    finally: 
     log_file.close(); 

def testme_backquotes_rule_yeah_baby(): 
    log_file = tempfile.TemporaryFile() 
    try: 
     for qmap in amap: 
      s = `qmap`+'\n' 
      log_file.write(s); 
    finally: 
     log_file.close();   

def testme_the_new_way_to_format(): 
    log_file = tempfile.TemporaryFile() 
    try: 
     for qmap in amap: 
      s = '{0} {1} {2} {3} {4} {5} \n'.format(qmap[0], qmap[1], qmap[2], qmap[3], qmap[4], qmap[5]) 
      log_file.write(s); 
    finally: 
     log_file.close(); 

# python 2.5 helper 
default_number = 50 
def _xtimeit(stmt="pass", timer=timeit.default_timer, 
      number=default_number): 
    """quick and dirty""" 
    if stmt<>"pass": 
     stmtcall = stmt+"()" 
     ssetup = "from __main__ import "+stmt 
    else: 
     stmtcall = stmt 
     ssetup = "pass" 
    t = timeit.Timer(stmtcall,setup=ssetup) 
    try: 
     return t.timeit(number) 
    except: 
     t.print_exc() 


# no formatting operation in testme2 

print "now timing variations on a theme" 

#times = [] 
#for i in range(0,10): 

n0 = _xtimeit("pass",number=50) 
print "pass = ",n0 

n1 = _xtimeit("testme_percent",number=50); 
print "old style % formatting=",n1 

n2 = _xtimeit("testme_tuple_percent",number=50); 
print "old style % formatting with tuples=",n2 

n3 = _xtimeit("testme_backquotes_rule_yeah_baby",number=50); 
print "backquotes=",n3 

n4 = _xtimeit("testme_the_new_way_to_format",number=50); 
print "new str.format conversion=",n4 


#  times.append(n); 




print "done"  

Creo que se podría optimizar el código mediante la construcción de sus tuplas de flotadores en otro lugar, donde quiera que construyó ese mapa, en primer lugar, crear su lista de tuplas, y luego aplicar el fmt_string% tupla de esta manera:

for tup in mytups: 
    log_file.write(fmt_str % tup) 

Pude afeitar los 8.7 segundos hasta 8.5 segundos dejando caer la parte de hacer una tupla fuera del ciclo for. Lo cual no es mucho. El chico grande es el formato de coma flotante, que creo que siempre va a ser costoso.

Alternativa:

¿ha considerado no escribir enormes troncos tales como texto, y en su lugar, el ahorro de ellos utilizando el método más rápido "persistencia" disponibles, y luego escribe una breve utilidad para volcar en texto, cuando sea necesario? Algunas personas usan NumPy con conjuntos de datos numéricos muy grandes, y no parece que utilicen un volcado línea por línea para almacenar sus cosas. Ver:

http://thsant.blogspot.com/2007/11/saving-numpy-arrays-which-is-fastest.html

+0

Este enfoque agrupa todo sobre lo que está sucediendo junto al tiempo, que no es la forma más efectiva de descubrir cómo varía el tiempo con un cambio. Por lo general, desea aislar solo las operaciones que está mirando. –

+0

El módulo 'tempfile' proporciona una manera confiable de crear archivos temporales sin introducir interferencias innecesarias con los archivos existentes o la dependencia de la plataforma. Además, 'si os.path.exists (...)' introduce una condición de carrera innecesaria sobre simplemente llamar a 'os.remove' y detectar el error, y en general es peor debido a esto. También típicamente la mala forma se basa en 'close' para llamarse manualmente; si realmente quiere asegurarse de que un archivo se cierre, use un administrador de contexto ('con open (afilename1, 'w') como log_file:'). El código actual no cerrará el archivo si se produce una excepción. –

+0

El uso de trazos para llamar a 'repr' está en desuso. Es difícil de leer y para algunas personas es difícil de escribir. Sus tres soluciones hacen cosas diferentes, pero esta es especialmente diferente de las otras. –

0

Sin querer meterse en el pantano-este código Optimizar, habría escrito el código de la misma familia:

log_file = open('testfile', 'w') 
x = start 
map_iter = zip(range(length), map[0], map[1], map[2], map[3], map[4], map[5]) 
fmt = '%-5d %8.3f %13g %13g %13g %13g %13g %13g\n' 
for i, m0, m1, m2, m3, m4, m5 in mapiter: 
    s = fmt % (i, x, m0, m1, m2, m3, m4, m5) 
    log_file.write(s) 
    x += interval 

Pero me pesará en la recomendación de que no nombra las variables después de órdenes internas pitón , como map.

Cuestiones relacionadas