Подтвердить что ты не робот

Почему str.translate намного быстрее в Python 3.5 по сравнению с Python 3.4?

Я пытался удалить ненужные символы из заданной строки, используя text.translate() в Python 3.4.

Минимальный код:

import sys 
s = '[email protected]#@$#%$'
mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$')
print(s.translate(mapper))

Работает так, как ожидалось. Однако одна и та же программа, выполняемая в Python 3.4 и Python 3.5, дает большую разницу.

Код для расчета таймингов

python3 -m timeit -s "import sys;s = '[email protected]#@$#%$'*1000 ; mapper = dict.fromkeys(i for i in range(sys.maxunicode) if chr(i) in '@#$'); "   "s.translate(mapper)"

Программа Python 3.4 принимает 1,3 мс, тогда как одна и та же программа в Python 3.5 принимает только 26.4μs.

Что улучшилось в Python 3.5, что делает его более быстрым по сравнению с Python 3.4?

4b9b3361

Ответ 1

TL; DR - ISSUE 21118


Длинная история

Джош Розенберг узнал, что функция str.translate() очень медленная по сравнению с bytes.translate, он поднял issue, указав что:

В Python 3, str.translate() обычно является пессимизацией производительности, а не оптимизацией.

Почему str.translate() медленный?

Основная причина того, что str.translate() была очень медленной, заключалась в том, что поиск использовался в словаре Python.

Использование maketrans затруднило эту проблему. Аналогичный подход с использованием bytes создает массив C из 256 элементов для быстрого поиска таблицы. Следовательно, использование более высокого уровня Python dict делает str.translate() в Python 3.4 очень медленным.

Что произошло сейчас?

Первый подход заключался в том, чтобы добавить небольшой патч, translate_writer, однако увеличение скорости было не таким приятным. Вскоре был протестирован другой патч fast_translate, и он дал очень хорошие результаты до 55% ускорения.

Основное изменение, которое можно увидеть из файла, заключается в том, что поиск словаря Python изменяется на поиск уровня C.

Скорости теперь почти такие же, как bytes

                                unpatched           patched

str.translate                   4.55125927699919    0.7898181750006188
str.translate from bytes trans  1.8910855210015143  0.779950579000797

Небольшая заметка заключается в том, что повышение производительности является только заметным в строках ASCII.

Как отмечает J.F.Sebastian в comment ниже, до 3.5, перевод используется так же, как для ASCII, так и для случаев, отличных от ASCII. Однако из 3.5 случая ASCII происходит намного быстрее.

Раньше ASCII против non-ascii использовалась почти одинаково, однако теперь мы можем наблюдать значительное изменение производительности.

Это может быть улучшение от 71,6 мкс до 2,33 мкс, как показано в этом .

Следующий код демонстрирует это

python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
100000 loops, best of 3: 2.3 usec per loop
python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 117 usec per loop

python3 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"
10000 loops, best of 3: 91.2 usec per loop
python3 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"
10000 loops, best of 3: 101 usec per loop

Подведение итогов:

         Python 3.4    Python 3.5  
Ascii     91.2          2.3 
Unicode   101           117

Ответ 2

Реализаторы Python сделали это быстрее.