web-dev-qa-db-fra.com

Comment grep fonctionne-t-il si vite?

Je suis vraiment étonné par la fonctionnalité de GREP dans Shell, auparavant, j'utilisais la méthode de sous-chaîne dans Java mais maintenant j'utilise GREP pour cela et il s'exécute en quelques secondes, c'est incroyablement plus rapide que Java code que j'avais l'habitude d'écrire. (selon mon expérience, je me trompe peut-être))

Cela étant dit, je n'ai pas pu comprendre comment cela se passe? il n'y a pas non plus grand chose disponible sur le web.

Est-ce que quelqu'un peut m'aider avec ça?

100
Dude

En supposant que votre question concerne spécifiquement GNU grep. Voici une note de l'auteur, Mike Haertel:

GNU grep est rapide car il ÉVITE DE REGARDER AT CHAQUE OCTET D'ENTRÉE.

GNU grep est rapide car il EXÉCUTE TRÈS PEU D'INSTRUCTIONS POUR CHAQUE BYTE qu'il regarde .

GNU grep utilise l'algorithme bien connu de Boyer-Moore, qui recherche d'abord la dernière lettre de la chaîne cible, et utilise une table de recherche pour lui dire dans quelle mesure il peut sauter dans l'entrée chaque fois qu'il trouve un caractère non correspondant.

GNU grep déroule également la boucle interne de Boyer-Moore et configure les entrées de la table delta de Boyer-Moore de telle sorte qu'il n'a pas besoin de faire le test de sortie de boucle à chaque étape non déroulée. Le résultat de ceci est que, dans la limite, GNU grep fait en moyenne moins de 3 instructions x86 exécutées pour chaque octet d'entrée qu'il regarde réellement (et il saute de nombreux octets entièrement).

GNU grep utilise des appels système d'entrée Unix bruts et évite de copier des données après les avoir lues. De plus, GNU grep ÉVITE DE BRISER L'ENTRÉE EN LIGNES. La recherche de nouvelles lignes ralentirait la grep d'un facteur de plusieurs fois, car pour trouver les nouvelles lignes, il faudrait regarder chaque octet!

Ainsi, au lieu d'utiliser une entrée orientée ligne, GNU grep lit les données brutes dans un grand tampon, recherche le tampon à l'aide de Boyer-Moore et ce n'est que lorsqu'il trouve une correspondance qu'il va chercher les nouvelles lignes de délimitation (commande Certain les options de ligne comme -n désactivent cette optimisation.)

Cette réponse est un sous-ensemble des informations tirées de ici .

154
Steve

Pour ajouter à l'excellente réponse de Steve.

Il n'est peut-être pas largement connu, mais grep est presque toujours plus rapide lors de la recherche d'un plus long chaîne de motif qu'une courte, car dans un modèle plus long, Boyer-Moore peut avancer dans des foulées plus longues pour atteindre encore mieux sublinéaire vitesses:

Exemple:

# 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

La forme plus longue est 35% plus rapide!

Comment venir? Boyer-Moore reconstruit une table de saut vers l'avant à partir de la chaîne de motif, et chaque fois qu'il y a un décalage, il sélectionne le saut le plus long possible (du dernier caractère au premier) avant de comparer un seul caractère dans le entrée au caractère dans la table de saut.

Voici ne vidéo expliquant Boyer Moore (Crédit à kommradHomer)

Une autre idée fausse courante (pour GNU grep) est que fgrep est plus rapide que grep. f in fgrep doesn ' t signifie "rapide", il signifie "fixe" (voir la page de manuel), et puisque les deux sont le même programme et utilisent tous les deux Boyer-Moore, il n'y a pas de différence de vitesse entre eux lors de la recherche de chaînes fixes sans regexp caractères spéciaux. La seule raison pour laquelle j'utilise fgrep est quand il y a un caractère spécial regexp (comme ., [], ou *) Je ne veux pas qu'il soit interprété comme tel. Et même alors, la forme la plus portable/standard de grep -F est préféré à fgrep.

36
arielf