Строковая производительность - Python 2.7 против Python 3.4 под Windows 10 против Ubuntu
Случай использования
Простая функция, которая проверяет, находится ли конкретная строка в другой строке в позиции, кратной 3 (см. Здесьпример из реального мира, нахождение стоп-кодонов в последовательности ДНК).
функцииsliding_window
: принимает строку длиной 3, сравнивает ее со строкой поиска, если они идентичны, перемещает на 3 символа вперед.incremental_start
: пытается найти строку поиска, если найденная позиция не кратна 3, она пытается найти следующую позицию после найденной позиции.
Обратите внимание: пример данных предназначен только для того, чтобы каждая функция прошла через всю строку, производительность схожа с реальными или случайными данными.
Результаты
Python 2.7: Начальныйsliding_window
функция может быть улучшена в ~ 39 раз с помощью функцииincremental_start
в Python2.7 на Windows 10. В Ubuntu наблюдалось небольшое снижение производительности, ~ 34x, ~ 37x, ~ 18x (VM, AWS, native), но все еще в том же диапазоне.Python 3.4:
sliding_window
стал медленнее, чем в Python2.7 (1.8x в Windows, 1.4x или 1.5x на всех Ubuntus), ноincremental_start
производительность снизилась на всех Ubuntus в 4, 5, 1,7 раза (VM, AWS, native), в то время как на Window s она практически не изменилась.Windows против Ubuntupython2.7: виртуализированной Ubuntus требовалось меньше времени для обеих функций (~ 20-30%), нативная Ubuntu была примерно на 25% медленнее для
incremental_start
, в то время какsliding_window
был на 40% быстрее.python3:
sliding_window
функции потребовалось меньше времени для завершения (~ 50%), в то время какincremental_start
стал медленнее в ~ 2-3 раза.Вопросы
Что вызывает разницу в производительности в Python 2 против Python 3 в Linux против Windows?Как можно предвидеть такое поведение и корректировать код для оптимальной производительности?Код
import timeit
text = 'ATG' * 10**6
word = 'TAG'
def sliding_window(text, word):
for pos in range(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def incremental_start(text, word):
start = 0
while start != -1:
start = text.find(word, start + 1)
if start % 3 == 0:
return False
return True
#sliding window
time = timeit.Timer(lambda: sliding_window(text, word), setup='from __main__ import text, word').timeit(number=10)
print('%3.3f' % time)
#incremental start
time = timeit.Timer(lambda: incremental_start(text, word), setup='from __main__ import text, word').timeit(number=500)
print('%3.3f' % time)
таблицы
Ubuntu vs Windows VM AWS Native
Python2.7-Increment 79% 73% 126%
Python2.7-Sliding 70% 70% 60%
Python3.4-Increment 307% 346% 201%
Python3.4-Sliding 54% 59% 48%
Py2 vs 3 Windows VM AWS Native
Increment 105% 409% 501% 168%
Sliding 184% 143% 155% 147%
Absolute times in seconds
Win10 Ubuntu AWS Native
Py2.7-Increment 1.759 1.391 1.279 2.215
Py2.7-Sliding 1.361 0.955 0.958 0.823
Py3.4-Increment 1.853 5.692 6.406 3.722
Py3.4-Sliding 2.507 1.365 1.482 1.214
подробности
Windows 10: родная Windows, 32-битный Python 3.4.3 или 2.7.9, i5-2500, 16 ГБ ОЗУ
Виртуальная машина Ubuntu: 14.04, работает на хосте Windows, 64-битный Python 3.4.3, Python 2.7.6, 4 ядра, 4 ГБ ОЗУ
AWS: 14.04, микроэкземпляр AWS, 64-битный Python 3.4.3, Python 2.7.6
Нативная Ubuntu: 14.04, 64-битный Python 3.4.3, Python 2.7.6, i5-2500, 16 ГБ ОЗУ [идентично машине с Win10]
По предложению Ингазаxrange
а такжеbytes
небольшое улучшение производительности, но все еще значительное падение производительности в Ubuntu с Python3.4. Виновным кажетсяfind
что гораздо медленнее, когда объединяются Ubuntu и Py3.4 (то же самое с Py3.5, который был скомпилирован из исходного кода). Это, похоже, зависит от Linux, на Debian Py2.7 и Py3.4 выполнялись одинаково, на RedHat Py2.7 был значительно быстрее, чем на Py3.4.
Для лучшего сравнения Py3.4 теперь используется в 64-битной версии на Windows10 и Ubuntu. Py27 все еще используется на Win10.
import timeit, sys
if sys.version_info >= (3,0):
from builtins import range as xrange
def sliding_window(text, word):
for pos in range(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def xsliding_window(text, word):
for pos in xrange(0, len(text), 3):
if text[pos:pos + 3] == word:
return False
return True
def incremental_start(text, word):
start = 0
while start != -1:
start = text.find(word, start + 1)
if start % 3 == 0:
return False
return True
text = 'aaa' * 10**6
word = 'aaA'
byte_text = b'aaa' * 10**6
byte_word = b'aaA'
time = timeit.Timer(lambda: sliding_window(text, word), setup='from __main__ import text, word').timeit(number=10)
print('Sliding, regular: %3.3f' % time)
time = timeit.Timer(lambda: incremental_start(text, word), setup='from __main__ import text, word').timeit(number=500)
print('Incremental, regular: %3.3f' % time)
time = timeit.Timer(lambda: sliding_window(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=10)
print('Sliding, byte string: %3.3f' % time)
time = timeit.Timer(lambda: incremental_start(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=500)
print('Incremental, bytes: %3.3f' % time)
time = timeit.Timer(lambda: xsliding_window(byte_text, byte_word), setup='from __main__ import byte_text, byte_word').timeit(number=10)
print('Sliding, xrange&bytes: %3.3f' % time)
time = timeit.Timer(lambda: text.find(word), setup='from __main__ import text, word').timeit(number=1000)
print('simple find in string: %3.3f' % time)
Win10-py27 Wi10-py35 VM-py27 VM-py34
1.440 2.674 0.993 1.368
1.864 1.425 1.436 5.711
1.439 2.388 1.048 1.219
1.887 1.405 1.429 5.750
1.332 2.356 0.772 1.224
3.756 2.811 2.818 11.361