Как grep работает так быстро?

Я действительно поражен функциональностью GREP в оболочке, раньше я использовал метод подстроки в java, но теперь я использую GREP для него, и он выполняется в считанные секунды, он невероятно быстрее, чем Java-код, который я использовал писать (по моему опыту я мог бы ошибаться, хотя)

Сказано, что мне не удалось выяснить, как это происходит? в Интернете также мало что доступно.

Может ли кто-нибудь помочь мне с этим?

Ответ 1

Предполагая, что ваш вопрос касается GNU grep. Здесь записка автора, Майк Хейртель:

GNU grep работает быстро, потому что AVOIDS LOOKING IN EVERY INPUT BYTE.

GNU grep быстро, потому что он ВЫПОЛНИЛ ОЧЕНЬ НЕСКОЛЬКИХ ИНСТРУКЦИЙ ДЛЯ КАЖДОГО БАЙТ, что он смотрит на.

GNU grep использует известный алгоритм Бойера-Мура, который выглядит первым для окончательной буквы целевой строки и использует таблицу поиска для расскажите, как далеко вперед он может пропустить вход, всякий раз, когда он находит несогласованный символ.

GNU grep также разворачивает внутренний цикл Boyer-Moore и устанавливает Записи в таблице дельфинов Бойер-Мура таким образом, что им не нужно выполните тест выхода цикла на каждом развернутом шаге. Результатом этого является что в пределе GNU grep составляет в среднем менее 3 инструкций x86 выполняется для каждого входного байта, на который он фактически смотрит (и он пропускает многие байты целиком).

GNU grep использует необработанные входные системные вызовы Unix и позволяет избежать копирования данных после прочтения. Кроме того, GNU grep AVOIDS BREAKING INPUT INTO ЛИНИИ. Поиск новых строк замедлял бы grep в несколько раз, потому что, чтобы найти новые строки, на которые нужно было смотреть каждый байт!

Поэтому вместо использования линейного ввода GNU grep считывает необработанные данные в большой буфер, выполняет поиск буфера с помощью Boyer-Moore и только тогда, когда он находит совпадение, и он ищет ограничивающие символы перевода строки (Некоторые параметры командной строки, например -n отключить эту оптимизацию.)

Этот ответ является подмножеством информации, взятой из здесь.

Ответ 2

Добавить Стиву отличный ответ.

Это может быть не очень широко известно, но grep почти всегда быстрее, когда ищет более длинную строку шаблона, чем короткую, потому что в более длинной схеме Бойер-Мур может переходить вперед более длинными шагами, чтобы достичь еще лучших сублинейных скоростей:

Пример:

# after running these twice to ensure apples-to-apples comparison
# (everything is in the buffer cache) 

$ time grep -c 'tg=f_c' 20140910.log
28
0.168u 0.068s 0:00.26

$ time grep -c ' /cc/merchant.json tg=f_c' 20140910.log
28
0.100u 0.056s 0:00.17

Более длинная форма на 35% быстрее!

Как так? Бойер-Мур создает таблицу пропуска -F orward из строки шаблона, и всякий раз, когда происходит несоответствие, он выбирает самый длинный возможный пропуск (от последнего символа к первому), прежде чем сравнивать один символ на входе с символом в пропуске. Таблица.

Вот видео, объясняющее Бойера Мура

Другое распространенное заблуждение (для GNU grep) заключается в том, что fgrep работает быстрее, чем grep. f в fgrep не означает "быстрый", он означает "фиксированный" (см. fgrep страницу), и поскольку обе они представляют собой одну и ту же программу и оба используют Бойера-Мура, при поиске различий в скорости между ними нет фиксированные строки без регулярных выражений специальных символов. Единственная причина, по которой я использую fgrep это когда есть специальный символ регулярного выражения (например, ., [] Или *), я не хочу, чтобы он интерпретировался как таковой. И даже тогда более переносимая/стандартная форма grep -F предпочтительнее, чем fgrep.