Может ли многопоточность или многопроцессорная обработка повысить производительность при анализе одной строки с несколькими регулярными выражениями?
Если я хочу проанализировать строку, используя десятки регулярных выражений,
Может ли многопроцессорный или многопроцессорный модуль улучшить производительность?
Другими словами, будет ли анализ строки в нескольких потоках или процессах быстрее, чем:
match = re.search(regex1, string)
if match:
afunction(match)
else:
match = re.search(regex2, string)
if match:
bfunction(match)
else:
match = re.search(regex3, string)
if match:
cfunction(match)
...
Не более одного регулярного выражения никогда не будет соответствовать, так что это не проблема.
Если ответ многопроцессорный, какую технику вы бы порекомендовали (очереди, каналы)?
3 ответа
Потоки Python не улучшат производительность из-за GIL, который исключает одновременное выполнение нескольких потоков. Если у вас многоядерный компьютер, возможно, что несколько процессов могут ускорить процесс, но только если стоимость порождения подпроцессов и передачи данных меньше, чем стоимость выполнения ваших поисков RE.
Если вы делаете это часто, вы можете заглянуть в пулы потоков.
Сами регулярные выражения - сильный ответ. Следующий пример может объединить все регулярные выражения в одно большое регулярное выражение. (В этом примере замените ваши регулярные выражения на a
,b
,c
а также d
,
(a?P<A>)|(b?P<B>)|(c?P<C>)|(d?P<D>))
использование lastindex
на MatchObject
чтобы узнать индекс группы, которая соответствует. использование groupindex
на RegexObject
перевести этот индекс в имя регулярного выражения, которое является меткой в угловых скобках (я использовал их в верхнем регистре в приведенном выше примере).
Изменить: (анализ производительности)
В тех случаях, когда используемые регулярные выражения достаточно просты, чтобы соответствовать обычным языкам, и, следовательно, могут быть быстро сопоставлены автоматом конечного состояния, этот подход фактически приведет к эффекту производительности, аналогичному параллельной оценке, что удивительно потребляет только один ресурс процессора.
Причина в том, что |
оператор, наряду с ?
, *
, []
или повторение (но в отличие от большинства использований обратных ссылок) является одним из операторов, разрешенных в регулярных выражениях, которые определяют обычные языки. (Обратите внимание на "объединение" в ссылке.) Поэтому объединенное регулярное выражение также можно искать с помощью конечного автомата, без необходимости возврата.
Автоматы с конечным состоянием проводят только конечное число операций с каждым символом входной строки. Они сохраняют состояние (в основном указатель памяти), которое представляет "потенциальный прогресс сопоставления" в текущей позиции ввода. В случае комбинированного регулярного выражения FSA больше, и компиляция занимает больше времени (и указатель памяти имеет больше областей памяти, на которые можно указать). Но это (создание Regex
объект) может быть сделано один раз при запуске приложения, и каждый последующий ввод может быть быстро найден.
Давайте сравним это с параллельным выполнением отдельных регулярных выражений на основе потоков. Прогресс в каждом регулярном выражении будет аналогичным, но не одинаковым для регулярных выражений, привязанных к началу ввода, особенно потому, что окончательные отклонения несоответствующих регулярных выражений обычно будут намного быстрее, чем успешные совпадения. Есть небольшое преимущество со стороны потоков: самое быстрое совпадение позволит прекратить все вычисления, тогда как объединенное регулярное выражение должно завершить оценку всех совпадений (всех групп). На практике накладные расходы пула потоков более чем компенсируют это, и при большом количестве потоков они будут в основном непригодны.
Таким образом, выигрыш в производительности комбинированной техники регулярных выражений особенно заметен при большом количестве регулярных выражений и оплачивается за счет увеличения потребления памяти.
Хотя это отвечает предпочтению этого вопроса параллельного сопоставления под капотом, в меньших случаях, таких как несколько регулярных выражений, это может не стоить дополнительной сложности составления регулярного выражения.
Производительность ваша забота? Если нет, просто поместите все RE в массив и зациклите его!
for each myRE in myListOfRE
result = myRE.search(...)
if result != None:
something with sqlalchemy
break
Если производительность действительно является проблемой, я думаю, многопоточность должна помочь. Для совпадения RE необходим только доступ на чтение к искомой строке, поэтому должна быть возможность поделиться ею. Хотя я не эксперт по питонам, поэтому не могу сказать, как это сделать.