2010-04-14 3 views
8

Я использую следующий код для регистрации карты, это быстро, когда он содержит только нули, но как только на карте появляются фактические данные, он становится невыносимо медленным ... Есть ли способ сделать это быстрее?Форматирование строки Python слишком медленно

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

Насколько велик набор данных на карте? Возможно, было бы проще почтить (map [0], map [1] и т. Д.), А затем прокручивать полученные кортежи. –

+0

@Josh Я не думаю, что это проблема, как сказано, это быстро, когда все нули (например, под второй), но невыносимо медленны с фактическим содержимым (например, две минуты для карты длины 600). – wich

+1

Вы уверены, что это связано с форматированием строк и не писать? – Francesco

ответ

3

Я предлагаю вам запустить свой код, используя cProfile модуль и постобработки результатов, как описано на http://docs.python.org/library/profile.html. Это позволит точно узнать, сколько времени потрачено на вызов str.__mod__ для форматирования строк и сколько потрачено на выполнение других действий, например, написание файла и поиск __getitem__ для map[0][i] и т. Д.

2

Сначала я проверил% против обратного отсчета. % быстрее. THEN Я проверил% (кортеж) против 'string'.format(). Первоначальная ошибка заставила меня подумать, что это было быстрее. Но нет. % быстрее.

Итак, вы уже делаете свою массивную кучу конверсий с плавающей точкой в ​​самый быстрый способ сделать это на Python.

Демо-код ниже - уродливый демо-код. Пожалуйста, не читайте мне лекции по xrange по сравнению с диапазоном или другим педантизмом. KThxBye.

Мое ad-hoc и очень ненаучное тестирование показывает, что (a)% (1.234,) операции на Python 2.5 на Linux быстрее, чем% (1.234, ...), операция Python 2.6 на linux, для тестового кода ниже , при условии, что попытка использовать 'string'.format() не будет работать в версиях python до 2.6. И так далее.

# 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"  

Я думаю, что вы могли бы оптимизировать свой код, построив свои кортежи поплавков где-то еще, где бы вы построили эту карту, в первую очередь, создать свой список кортежей, а затем применяя fmt_string% кортежа так:

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

Я смог побрить 8,7 секунды до 8,5 секунд, отбросив часть make-a-tuple из цикла for. Это немного. У большого мальчика есть форматирование с плавающей запятой, которое, я считаю, всегда будет дорогостоящим.

Альтернатива:

Рассматривали ли вы НЕ писать такие огромные бревна, как текст, а вместо этого, сохранив их, используя быстрый метод «живучесть» доступен, а затем писать короткую программу, чтобы сбросить их в текст, когда это необходимо? Некоторые люди используют NumPy с очень большими числовыми наборами данных, и, похоже, они не будут использовать построчный дамп для хранения своего материала. См:

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

+0

Этот подход объединяет все, что происходит вместе во времени, что не является самым эффективным способом выяснить, как меняется время с изменением. Обычно вы хотите изолировать только те операции, на которые вы смотрите. –

+0

Модуль 'tempfile' обеспечивает надежный способ создания временных файлов, не создавая ненужных помех существующим файлам или зависимости от платформы. Кроме того, 'if os.path.exists (...)' вводит ненужное условие гонки за просто вызов 'os.remove' и ловить ошибку и, как правило, хуже из-за этого. Также типично плохая форма полагается на 'close' для вызова вручную; если вы действительно хотите убедиться, что файл закрыт, используйте диспетчер контекста ('with open (afilename1, 'w') как log_file:'). Текущий код не будет закрывать файл, если возникло исключение. –

+0

Использование backticks для вызова 'repr' устарело. Трудно читать, а некоторым людям трудно печатать. Все три ваших решения делают разные вещи, но это особенно отличается от других. –

0

Не желая лезть в оптимизируют-это-кода трясины, я бы написал код больше, как это:

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 

Но я буду весить с рекомендацией, что не имя переменной после питона встроенных команд , как map.

Смежные вопросы