Чому str.translate набагато швидше в Python 3.5 порівняно з Python 3.4?


116

Я намагався видалити небажані символи з заданої рядки за допомогою програми text.translate()Python 3.4.

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

import sys 
s = 'abcde12345@#@$#%$'
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 = 'abcde12345@#@$#%$'*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 мкс .

Що покращилося в Python 3.5, що робить його швидшим порівняно з Python 3.4?


11
Поки ми говоримо про ефективність, чи не було б краще створити ваш картограф таким чином dict.fromkeys(ord(c) for c in '@#$'):?
Томас К

1
@ThomasK Я з'ясував, що це суттєво змінилося. Так, ваш шлях краще.
Bhargav Rao

Ви мали на увазі 50 разів швидше?
assylias

@assylias Я зробив 1300 - 26,4, а потім поділився на 1300. У мене майже 95%, тому я писав :) Насправді більш ніж на 50 разів швидше ... Але чи мій розрахунок помилився? Я трохи слабкий з математики. Я скоро навчусь математиці. :)
Bhargav Rao

3
ви повинні зробити це навпаки: 26/1300 = 2%, тому більш швидка версія займає лише 2% часу, зайнятого повільнішою версією => це на 50 разів швидше.
Ассілія

Відповіді:


148

TL; DR - ВИПУСК 21118


Довга історія

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

У Python 3 str.translate()зазвичай песимізація продуктивності, а не оптимізація.

Чому str.translate()повільно?

Основна причина str.translate()бути дуже повільною була в тому, що пошук знаходився в словнику Python.

Використання maketransзробило цю проблему ще гірше. Аналогічний підхід з використанням bytesстворює масив С з 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.

Як згадує JFSebastian у коментарі нижче, до 3.5, переклад використовувався для роботи однаково і для ASCII, і для випадків, що не належать до ASCII. Однак з 3.5 ASCII справа йде набагато швидше.

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

Це може бути поліпшення з 71,6μs до 2,33μs, як видно з цієї відповіді .

Наступний код демонструє це

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

13
Це один із комітетів
фільм

Примітка: випадок ascii vs. non-ascii може суттєво відрізнятися за ефективністю. Це не про 55%: як показує ваша відповідь, швидкість може бути 1000s% .
jfs

порівняти: python3.5 -m timeit -s "text = 'mJssissippi'*100; d=dict(J='i')" "text.translate(d)"(ascii) vs. python3.5 -m timeit -s "text = 'm\U0001F602ssissippi'*100; d={'\U0001F602': 'i'}" "text.translate(d)"(non-ascii). Останнє значно (в 10 разів) повільніше.
jfs

@JF О, я це зрозумів зараз. Я провів ваш код як для 3.4, так і для 3.5. Я отримую Py3.4 швидше для не-ascii речі. Це за збігом обставин? Результати dpaste.com/15FKSDQ
Bhargav Rao

До 3.5 випадки, і випадки ascii, і non-ascii, ймовірно, однакові для Unicode, .translate()тобто випадок ascii набагато швидший лише в Python 3.5 (там вам не потрібна bytes.translate()продуктивність).
jfs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.