[Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 19.07.10 05:45
Оценка:
Понадобилось мне как-то сделать из таблички в питоне html. Табличка объемная вопрос оптимизации важен.

А тут на хабре статья забавная появилась. Там в комментах рекомендуют использовать ''.join(list) вместо str += str.

Решил последовать совету и переписал свою функцию. А теперь самое интересное: соединение юникодных строк происходит действительно раз в 10 быстрее, но вот для ansi-строк проигрывает. Хотелось бы узнать почему так?

Вот полный код теста:

from time import time

printHeader = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"/></head>
<body><h1>%s</h1><table cellspacing="0" cellpadding="0">
"""

s1 = (u'Тест', u'Еще тест')*8
s1 = [i.encode('cp1251') for i in s1]
s2 = [s1]*500

def table2html1(src, s1, printHeader):
    s = printHeader % 'dfgsdfgsdfg'
    s += '<tr>'
    for col in s1:
        s += '<td>%s&nbsp</td>'%col
    s += '</tr>\n'

    for row in src:
        s += '<tr>'
        for col in row:
            s += '<td>%s&nbsp</td>'%col
        s += '</tr>\n'
    
    s += "</table></body></html>"            
    return s

def table2html2(src, s1, printHeader):
    mkRow = lambda row: ''.join(sum((('<td>', col, '&nbsp</td>') for col in row), ()))
    mkDoc = lambda doc: ''.join(sum((('<tr>', mkRow(row), '</tr>\n') for row in doc), ()))
    return ''.join((printHeader % 'dfgsdfgsdfg', mkDoc([s1]), mkDoc(src), '</table></body></html>'))

from cStringIO import StringIO

def table2html3(src, s1, printHeader):
    s = StringIO()
    s.write(printHeader % 'dfgsdfgsdfg')

    def mkDoc(doc):
        for row in doc:
            s.write('<tr>')
            for col in row:
                s.write('<td>')
                s.write(col)
                s.write('&nbsp</td>')
            s.write('</tr>\n')
            
    mkDoc([s1])
    mkDoc(src)
    
    s.write("</table></body></html>")
    return s.getvalue()

t = time()
result1 = table2html1(s2, s1, printHeader)
print time()-t

t = time()
result2 = table2html2(s2, s1, printHeader)
print time()-t

t = time()
result3 = table2html3(s2, s1, printHeader)
print time()-t

with open('join1.html', 'wb') as fd:
    fd.write(result1)

with open('join2.html', 'wb') as fd:
    fd.write(result2)
    
with open('join3.html', 'wb') as fd:
    fd.write(result3)


Тут еще одна версия есть с использованием cStringIO — table2html3.

Результаты:
0.00999999046326
0.0150001049042
0.0190000534058


Если закоментировать вверху s1 = [i.encode('cp1251') for i in s1]:
0.648000001907
0.018000125885
Traceback (most recent call last): # cStringIO с юникодом не работает, нужно оставить StringIO, но он медленнее.


Или я что-то упустил?
Re: [Python] Оптимизация соединения множества строк
От: ShaggyOwl Россия http://www.rsdn.org
Дата: 19.07.10 06:50
Оценка: 2 (1)
Здравствуйте, Critical Error, Вы писали:

CE>Понадобилось мне как-то сделать из таблички в питоне html. Табличка объемная вопрос оптимизации важен.


Полуофф в помощь. В закромах ссылочка завалялась Efficient String Concatenation in Python http://www.skymind.com/~ocrow/python_string/ (т.к. дата публикации 2004-05-01, имеет смысл перепроверить инфу в ней)
Хорошо там, где мы есть! :)
Re: [Python] Оптимизация соединения множества строк
От: Temoto  
Дата: 19.07.10 07:58
Оценка: 1 (1)
CE>Понадобилось мне как-то сделать из таблички в питоне html. Табличка объемная вопрос оптимизации важен.

CE>А тут на хабре статья забавная появилась. Там в комментах рекомендуют использовать ''.join(list) вместо str += str.


CE>Решил последовать совету и переписал свою функцию. А теперь самое интересное: соединение юникодных строк происходит действительно раз в 10 быстрее, но вот для ansi-строк проигрывает. Хотелось бы узнать почему так?


CE>Если закоментировать вверху s1 = [i.encode('cp1251') for i in s1]:


Если s1 будет юникодом, то и джойнить надо тоже юникодовой строкой.

u"".join(...)

Иначе происходит неявное кодирование, которое может быть причиной медленности.

Вообще же, для решения вашей задачи хорошо подходит (2.5 и ниже) string.format, (2.6 и выше) str/unicode.format, mako http://www.makotemplates.org/ , Jinja2 http://jinja.pocoo.org/2/
Re[2]: [Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 19.07.10 21:14
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Если s1 будет юникодом, то и джойнить надо тоже юникодовой строкой.


T>u"".join(...)


T>Иначе происходит неявное кодирование, которое может быть причиной медленности.


def table2html1u(src, s1, printHeader):
    s = printHeader % 'dfgsdfgsdfg'
    s = s.decode('utf-8')
    s += u'<tr>'
    for col in s1:
        s += u'<td>%s&nbsp</td>'%col
    s += u'</tr>\n'

    for row in src:
        s += u'<tr>'
        for col in row:
            s += u'<td>%s&nbsp</td>'%col
        s += u'</tr>\n'
    
    s += u"</table></body></html>"            
    return s


Результат тот же. В общем я сделал вывод, что внутри питона операция += хорошо оптимизирована только для ANSI-строк, в то время как для unicode она выполняется по канонам неизменных строк, то есть sNew = sOld1 + sOld2 с дереференсом обоих старых строк и чисткой sOld1 после завершения операции.

В общем я выбираю вариант с join — он хоть и чуток медленнее, но зато скорость работы не зависит от типа выбранных строк.

T>Вообще же, для решения вашей задачи хорошо подходит (2.5 и ниже) string.format, (2.6 и выше) str/unicode.format, mako http://www.makotemplates.org/ , Jinja2 http://jinja.pocoo.org/2/


Шаблонный двиг использовать для этой простенькой задачки считаю не слишком эффективным.
Re[3]: [Python] Оптимизация соединения множества строк
От: Temoto  
Дата: 19.07.10 21:26
Оценка:
CE>Результат тот же. В общем я сделал вывод, что внутри питона операция += хорошо оптимизирована только для ANSI-строк, в то время как для unicode она выполняется по канонам неизменных строк, то есть sNew = sOld1 + sOld2 с дереференсом обоих старых строк и чисткой sOld1 после завершения операции.

CE>В общем я выбираю вариант с join — он хоть и чуток медленнее, но зато скорость работы не зависит от типа выбранных строк.


Увышка, я думал будет разница.

T>>Вообще же, для решения вашей задачи хорошо подходит (2.5 и ниже) string.format, (2.6 и выше) str/unicode.format, mako http://www.makotemplates.org/ , Jinja2 http://jinja.pocoo.org/2/


CE>Шаблонный двиг использовать для этой простенькой задачки считаю не слишком эффективным.


Эффективность меряется не "считаю" или простотой задачи, а бенчмарками. Вы же не знаете как эти движки реализованы. Может быть они быстрее отработают, чем += или даже join.

В качестве бонуса получите поддерживаемый код, с которым во много раз удобнее работать, чем с этим ужасом из циклов и кавычек.
Re[2]: [Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 19.07.10 21:54
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Вообще же, для решения вашей задачи хорошо подходит (2.5 и ниже) string.format, (2.6 и выше) str/unicode.format, mako http://www.makotemplates.org/ , Jinja2 http://jinja.pocoo.org/2/


Провел дополнительно тест для DTL:

def table2htmlDTL(src, s1, printHeader):
    tmpl = Template("""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>
<meta http-equiv="Content-Type" content="text/html; charset=windows-1251"/></head>
<body><h1>{{ hh }}</h1><table cellspacing="0" cellpadding="0">
{% for row in tbl %}
<tr>{% for col in row %}<td>{{ col }}&nbsp</td>{% endfor %}</tr>
{% endfor %}
</table></body></html>""")

    c = Context({"hh": 'dfgsdfgsdfg', 'tbl': src})
    return tmpl.render(c)


Результат для ANSI и unicode одинаков: 0.75, то есть сравним с соединением операцией += для unicode. Зато код красивый и универсальный.
Re[4]: [Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 19.07.10 22:01
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Эффективность меряется не "считаю" или простотой задачи, а бенчмарками. Вы же не знаете как эти движки реализованы. Может быть они быстрее отработают, чем += или даже join.


T>В качестве бонуса получите поддерживаемый код, с которым во много раз удобнее работать, чем с этим ужасом из циклов и кавычек.


Тест для DTL я привел ранее, он оказался медленнее. Но код красив, да...

Дело в том что это добавит зависимость в довольно простой код. Эту зависимость придется тянуть с моей программой постоянно. К тому же чудес не бывает... Шаблонизаторы внутри устроены примерно одинаково: сначала происходит разбивка на токены скорее всего регулярными выражениями, затем подставляются переменные и происходит тот же самый join. Конечно всякое бывает, но врядли есть схема быстрее этой на питоне... Насколько я понял по исходникам, именно так работает DTL.
Re[3]: [Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 19.07.10 22:14
Оценка:
Здравствуйте, Critical Error, Вы писали:

CE>Результат для ANSI и unicode одинаков: 0.75, то есть сравним с соединением операцией += для unicode. Зато код красивый и универсальный.


Ошибочка. DTL не работает с ANSI, на месте полей в табличке оказалось пусто.
Re[5]: [Python] Оптимизация соединения множества строк
От: Temoto  
Дата: 20.07.10 08:16
Оценка:
T>>Эффективность меряется не "считаю" или простотой задачи, а бенчмарками. Вы же не знаете как эти движки реализованы. Может быть они быстрее отработают, чем += или даже join.

T>>В качестве бонуса получите поддерживаемый код, с которым во много раз удобнее работать, чем с этим ужасом из циклов и кавычек.


CE>Тест для DTL я привел ранее, он оказался медленнее. Но код красив, да...


CE>Дело в том что это добавит зависимость в довольно простой код. Эту зависимость придется тянуть с моей программой постоянно. К тому же чудес не бывает... Шаблонизаторы внутри устроены примерно одинаково: сначала происходит разбивка на токены скорее всего регулярными выражениями, затем подставляются переменные и происходит тот же самый join. Конечно всякое бывает, но врядли есть схема быстрее этой на питоне... Насколько я понял по исходникам, именно так работает DTL.


Именно поэтому я не советовал тормозную Django. Посоветовал несколько конкретных решений, которые знамениты скоростью.
Re[6]: [Python] Оптимизация соединения множества строк
От: Critical Error ICQ: 123736611
Дата: 20.07.10 21:47
Оценка:
Здравствуйте, Temoto, Вы писали:

T>Именно поэтому я не советовал тормозную Django. Посоветовал несколько конкретных решений, которые знамениты скоростью.


Хм, и правда, Jinja2 выдала очень хороший результат, раз в 30 быстрее DTL. При этом код шаблона остался таким же.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.