Як греп біжить так швидко?


113

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

Якщо говорити, я не зміг зрозуміти, як це відбувається? в Інтернеті також мало доступного.

Хтось може мені допомогти у цьому?


5
Це відкритий код, щоб ви могли самі поглянути. gnu.org/software/grep/devel.html
driis

6
Смішні риби мають чудовий опис, що відповідає саме на ваше запитання: ridiculousfish.com/blog/posts/old-age-and-treachery.html
Девід Волвер

@WilliamPursell Коли час виконання триває в секундах, JIT, ймовірно, прогрівся, а розумова різниця обумовлена ​​тим, що (1) grep є надзвичайно розумним щодо того, що він робить, і (2) код Java робить досить поганий вибір алгоритму для конкретної проблеми grep зосереджується на.

3
Скільки часу витрачає ваша реалізація Java, запускаючи JVM, і скільки часу вона фактично витрачає на виконання вашого коду? Або це може бути питанням алгоритму, який ви використовували в коді Java; алгоритм O (N ^ 2), ймовірно, буде повільним на будь-якій мові.
Кіт Томпсон

Відповіді:


169

Припустимо, що ваше питання стосується GNU grepконкретно. Ось примітка автора Майка Хаертеля:

GNU grep - це швидке, оскільки воно ВИМОГАЄ ГОЛОСУВАТИ НА ВСІЙ ВХОДНІЙ БАЙТ.

GNU Grep дуже швидко , тому що він виконує дуже мало інструкції для кожного BYTE , що робить погляд на.

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

GNU grep також розгортає внутрішню петлю Boyer-Moore і встановлює записи дельти таблиці Boyer-Moore таким чином, що їй не потрібно робити тест виходу з циклу на кожному розкрученому кроці. Результат цього полягає в тому, що, за межами, GNU grep в середньому менше 3 x86 інструкцій, виконаних для кожного вхідного байта, який він насправді переглядає (і він пропускає багато байтів цілком).

GNU grep використовує необмежену систему вхідних викликів Unix і уникає копіювання даних після їх читання. Більше того, GNU греп уникає ВРУШЕННЯ В ЛІНІЙ. Шукання нових рядків уповільнить показник у кілька разів, тому що для пошуку нових рядків потрібно було б переглянути кожен байт!

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

Ця відповідь є підмножиною інформації, взятої звідси .


41

Щоб додати до відмінної відповіді Стіва.

Це може не бути широко відомим, але grep майже завжди швидше, коли прихоплюється на довший шаблон-рядок, ніж короткий, тому що в більш тривалому шаблоні Boyer-Moore може пропускати вперед більш тривалими кроками, щоб досягти ще кращих підлінійних швидкостей:

Приклад:

# 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% швидша!

Як це? Бойєр-Мур обчислює таблицю пропускання вперед із рядка шаблону, і коли виникає невідповідність, він вибирає найдовший можливий пропуск (від останнього символу до першого), перш ніж порівнювати одну таблицю вхідних даних до таблиці в таблиці пропуску.

Ось відео з поясненням Бойєра Мура (заслуга kommradHomer)

Ще одна поширена помилкова думка (для GNU grep) - fgrepце швидше, ніж grep. fв fgrepне означає "швидкий", він означає "фіксований" (див. головну сторінку), і оскільки обидва є однією і тією ж програмою, і обидва використовують Boyer-Moore , немає різниці у швидкості між ними при пошуку фіксованих- рядки без спеціальних символів. Єдина причина , чому я використовувати fgrep, коли є регулярний вираз спеціальний символ (наприклад ., []чи *) Я не хочу, щоб це було витлумачено як такої. І навіть тоді більш портативна / стандартна форма grep -Fкраще fgrep.


3
Інтуїтивно зрозуміло, що довші візерунки швидше. Якщо шаблон був одним байтом, тоді grep повинен був би перевірити кожен байт. Якщо шаблон є 4-байтовим, він може робити 4-байтові пропуски. Якщо шаблон був довгим тексту, то grep робив би лише один крок.
Ноель

12
Так, це інтуїтивно - якщо ви розумієте, як працює Бойєр-Мур.
аріельф

2
Навіть інакше це інтуїтивно. Простіше було б знайти довгу голку в копиці сіна, ніж коротшу
RajatJ

2
Приклад зустрічного "швидше, коли довше" - це випадки, коли вам доведеться зробити багато тестів, перш ніж вийти з ладу, і ніяк не можете рухатися вперед. Скажімо, файл xs.txtмістить 100000000 'x, і ви зробите grep yx xs.txt, тоді він насправді не зможе знайти відповідність раніше, ніж якщо ви зробите grep yxxxxxxxxxxxxxxxxxxx xs.txt. Удосконалення Boyer-Moore-Horspool до Boyer-Moore в цьому випадку покращується вперед, але, мабуть, це не буде лише три інструкції з машини в загальному випадку.
lrn

2
@Tino спасибі Так, здається, що дні (GNU), що grep/fgrep/egrepє усіма жорсткими посиланнями на один і той же виконуваний файл, минули. Вони (та інші розширення, такі як z*grep bz*grepутиліти, які декомпресуються на льоту), тепер є невеликими оболонками grep. Деякі цікаві історичні коментарі щодо переключення між однією виконуваною програмою та оболонками оболонки можна знайти у цій комісії
arielf
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.