Python Regex медленнее, чем ожидалось

Я прочитал классную статью о том, как избежать создания медленных регулярных выражений. Вообще говоря, это выглядит как более длинный и более явный, а регулярное выражение - быстрее. Жадное регулярное выражение может быть экспоненциально медленнее.

Я думал, что смогу проверить это, измерив время, необходимое для завершения более сложного/явного заявления с менее сложным/жадным утверждением. По большей части все, кажется, держится, но у меня есть одно жадное утверждение, которое замедлялось медленнее. Вот два примера:

import re
from timeit import timeit

# This works as expected, the explicit is faster than the greedy.
# http_x_real_ip explicit 
print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
1.159849308001867

# http_x_real_ip greedy
print(timeit(setup="import re", stmt='''r = re.search(r'((?:\d{1,3}\.){3}\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
1.7421739230003368

# This does not work as expected, greedy is faster.
# time_local explicit
print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,2}/\w{3}/[2][0]\d{2}:\d{2}:\d{2}:\d{2}\s[+][0]{4})', "[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
1.248802040994633

# time_local greedy
print(timeit(setup="import re", stmt='''r = re.search(r'\[(.*)\]', "[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
1.0256699790043058

Является ли local_time explict регулярным выражением, которое плохо написано?

Ответ 1

Чем больше регулярное выражение должно отступать, тем медленнее оно.

Это может не соответствовать очень маленьким входным данным. Однако кто будет заботиться о производительности на небольших данных?: Dсуб >


Этот раздел хорошо освещен в этой статье:

Также есть интересные вклады в этом вопросе:

Ответ 2

Вы также не используете функцию re.compile для регулярных выражений Python, что означает, что ваше время поиска также включает время для повторного модуля для компиляции regex на каждой итерации.

>>> print(timeit(setup="import re", stmt='''r = re.search(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})', '192.168.1.1 999.999.999.999')''', number=1000000))
0.73820400238
>>> print(timeit(setup="import re; regex = re.compile(r'(\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3})')", stmt='''r = regex.search('192.168.1.1 999.999.999.999')''', number=1000000))
0.271140813828
>>> print(timeit(setup="import re; regex = re.compile(r'((?:\d{1,3}\.){3}\d{1,3})')", stmt='''r = regex.search('192.168.1.1 999.999.999.999')''', number=1000000))
0.31952214241
>>> print(timeit(setup="import re; regex = re.compile(r'(\d{1,2}/\w{3}/[2][0]\d{2}:\d{2}:\d{2}:\d{2}\s[+][0]{4})')", stmt='''r = regex.search("[23/Jun/2015:11:10:57 +0000]")''', number=1000000))
0.371844053268
>>> 

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

Мы видим, что ваши тесты ускоряются почти в 3 раза, если вы предварительно скомпилируете свои регулярные выражения для большого количества итераций.

Этот ответ предназначен для дополнения ответа @mescalinum, но для большого количества регулярных выражений вы должны действительно компилировать регулярные выражения за время для справедливого сравнения.