производительность regex.h

Я пытаюсь выяснить нелогичную разницу в производительности между Python, Cython и чистым C с сопоставлением регулярных выражений.

Существует небольшая примерная программа, которая берет исходный текстовый файл (17 КБ), словарь из 2000 слов, создает регулярное выражение с этими словами (слово1|слово2|...) и находит все экземпляры указанного словаря в исходном файле. .

Во-первых, я сделал чистую реализацию Python, которая выглядит так:

def scanFile(filename, patterns):
   pattern_regex = re.compile('|'.join(patterns))
   pageContent = open(filename).read()
   matchingPatterns = set()
   for matchObj in pattern_regex.finditer(pageContent):
      matchingPatterns.add(matchObj.group(0))

   return matchingPatterns

Затем я попытался оптимизировать это, повторно реализовав то же самое с Cython, поверх модуля regex.h, а не модуля re Python.

cdef extern from "regex.h" nogil:
   ctypedef struct regmatch_t:
      int rm_so
      int rm_eo
   ctypedef struct regex_t:
      pass
   int REG_EXTENDED
   int regcomp(regex_t* preg, const char* regex, int cflags)
   int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags)
   void regfree(regex_t* preg) 

def matchPatterns(bytes pageContent, bytes regex):
   cdef set matchingPatterns = set()
   cdef regex_t regex_obj
   cdef regmatch_t regmatch_obj[1]
   cdef int regex_res = 0
   cdef int current_str_pos = 0

   regcomp(&regex_obj, regex, REG_EXTENDED)
   regex_res = regexec(&regex_obj, pageContent[current_str_pos:], 1, regmatch_obj, 0)
   while regex_res == 0:
      matchingPatterns.add(pageContent[current_str_pos + regmatch_obj[0].rm_so: current_str_pos + regmatch_obj[0].rm_eo])
      current_str_pos += regmatch_obj[0].rm_eo
      regex_res = regexec(&regex_obj, pageContent[current_str_pos:], 1, regmatch_obj, 0)

   regfree(&regex_obj)
   return matchingPatterns

Однако производительность оказалась как раз наоборот: Cython+regex.h занимает около 2,34 с, а Python — 0,92 с.

После небольшого профилирования и пользовательского закомментированного кода я подтвердил подозрение, что он равен regexec, что занимает 10 миллисекунд при каждом вызове.

Просто чтобы убедиться, что виноват не Cython, подготовил автономный модульный тест C, который использует те же входные данные и regex.h, и он также показал худшие результаты, чем Python (около 1,60 с, т.е. на 60% медленнее, чем Python).

Итак, учитывая все это, я был бы признателен за любое понимание того, почему regexec имеет такую ​​низкую производительность.

Я использую это на Python 2.7.10, gcc 4.9.2, Cython 0.22 и платформе Cygwin/Windows. У меня было подобное несоответствие при запуске того же на Ubuntu.


person RomanK    schedule 26.06.2015    source источник
comment
У вас есть код для автономного модульного теста C?   -  person nhahtdh    schedule 26.06.2015
comment
Почему вы предполагаете, что механизм регулярных выражений C быстрее, чем те, которые используются в различных реализациях Python? Все они, вероятно, работают как собственный код C, и существует множество возможных вариантов реализации механизма регулярных выражений.   -  person unwind    schedule 26.06.2015
comment
Вам, вероятно, понадобится более широкий спектр RE, прежде чем вы сможете делать поспешные выводы о производительности. Возможно, вы нашли слабое место в одном RE-движке, но упустили его сильные стороны просто потому, что пробуете только OR. Может быть задействован другой код, например. вы используете set() в Python, вы делали то же самое в C?   -  person cdarke    schedule 26.06.2015
comment
Одна из возможных реализаций BRE/ERE состоит в том, чтобы реализовать его поверх механизма поиска с возвратом — и вместо узла принятия в конце он отслеживает самое длинное совпадение и позволяет механизму продолжать попытки, пока он не исчерпает все возможности. В результате такая реализация выполняет больше работы, чем механизм поиска с возвратом. Что касается причины этого - поскольку BRE поддерживает обратную ссылку, в любом случае необходимо реализовать механизм поиска с возвратом. Возможно, они не удосужились включить эффективную реализацию шаблонов без обратной ссылки.   -  person nhahtdh    schedule 26.06.2015
comment
@unwind - в основном потому, что производительность python re была намного хуже, чем boost::regex. Так как я хотел попробовать Cython (как опыт обучения, так и оптимизация), я выбрал regex.h, так как это чистый C. Я понимаю, что C++ можно включить в Cython и что boost::regex и regex.h не рождаются равными, но я надеялся получить одинаковые результаты в обоих случаях. Несоответствие вызвало вопрос. @cdarke - Действительно, вполне может быть, что с большим шаблоном OR Python справляется лучше. Однако из документации regex.h неясно, что он не хорошо справляется с этим.   -  person RomanK    schedule 26.06.2015
comment
@nhahtdh - Вероятно, в вашем ответе есть основная причина. Код C очень похож на Cython, но я могу добавить его, если потребуется. Как упоминалось в других комментариях, кажется, что механизм регулярных выражений Python просто лучше справляется с таким шаблоном (и boost::regex даже быстрее).   -  person RomanK    schedule 26.06.2015


Ответы (1)


Основываясь на вопросе, я могу предположить несколько проблем: - Вы используете POSIX в Windows, а Cygwin - это накладные расходы, Windows не является системой POSIX. -Есть сравнение между pcre (допустим, pcre2) и regex.h -Автономный скомпилированный код отличается от экспортируемых функций (компилятор ничего не может предположить) капот. - параметры компиляции и потенциальное сглаживание всегда трудно сравнивать.

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

И извините за эту часть, не связанную с вопросом как таковым, но похоже, что вам не нужен RE, а базовый алгоритм сопоставления строк или какая-то аккуратная структура, такая как дерево префиксов и простой цикл, для выполнения вашей задачи.

person Evil    schedule 26.07.2015