L'utilitaire ugrep-indexer indexe les fichiers de manière récursive pour accélérer la recherche récursive.
De plus, le contenu des archives et des fichiers compressés est indexé lorsqu'il est spécifié avec une option de ligne de commande. Cela élimine la recherche lorsqu'aucun de leurs contenus ne correspond aux modèles spécifiés.
ugrep est un outil de recherche de fichiers rapide compatible grep qui prend en charge la recherche basée sur un index. La recherche basée sur l'index peut être beaucoup plus rapide sur les systèmes de fichiers lents et lorsque la mise en cache du système de fichiers est inefficace : si le système de fichiers sur un lecteur recherché n'est pas mis en cache dans la RAM, c'est-à-dire s'il est « froid », alors l'indexation accélérera la recherche. Il recherche uniquement les fichiers pouvant correspondre à un modèle d'expression régulière spécifié en utilisant un index du fichier. Cet index permet de vérifier rapidement s'il existe une correspondance potentielle, nous évitons ainsi de rechercher tous les fichiers.
La recherche indexée avec ugrep est sûre et n'ignore jamais les fichiers mis à jour qui peuvent désormais correspondre. Si des fichiers et des répertoires sont ajoutés ou modifiés après l'indexation, la recherche recherchera toujours ces ajouts et modifications apportés au système de fichiers en comparant les horodatages des fichiers et des répertoires à l'horodatage d'indexation.
Lorsque de nombreux fichiers sont ajoutés ou modifiés après l'indexation, nous souhaiterons peut-être les réindexer pour mettre les index à jour. La réindexation est incrémentielle, elle ne prendra donc pas autant de temps que le processus d'indexation initial.
Un exemple typique mais petit de recherche basée sur un index, par exemple sur le référentiel ugrep v3.12.6 placé sur un lecteur séparé :
$ cd drive/ugrep
$ ugrep-indexer -I
12247077 bytes scanned and indexed with 19% noise on average
1317 files indexed in 28 directories
28 new directories indexed
1317 new files indexed
0 modified files indexed
0 deleted files removed from indexes
128 binary files ignored with --ignore-binary
0 symbolic links skipped
0 devices skipped
5605227 bytes indexing storage increase at 4256 bytes/file
La recherche normale sur un système de fichiers froid sans indexation prend 1,02 secondes après le démontage du drive
et son nouveau montage pour vider le cache FS afin d'enregistrer l'effet de l'indexation :
$ ugrep -I -l 'std::chrono' --stats
src/ugrep.cpp
Searched 1317 files in 28 directories in 1.02 seconds with 8 threads: 1 matching (0.07593%)
Ripgrep 13.0.0 prend plus de temps avec 1,18 secondes pour la même recherche à froid (ripgrep ignore les fichiers binaires par défaut, donc l'option -I
n'est pas spécifiée) :
$ time rg -l 'std::chrono'
src/ugrep.cpp
1.18 real 0.01 user 0.06 sys
En revanche, avec l'indexation, la recherche d'un système de fichiers froid ne prend que 0,0487 seconde avec ugrep, ce qui est 21 fois plus rapide, après avoir démonté drive
et remonté pour vider le cache FS afin d'enregistrer l'effet de l'indexation :
$ ugrep --index -I -l 'std::chrono' --stats
src/ugrep.cpp
Searched 1317 files in 28 directories in 0.0487 seconds with 8 threads: 1 matching (0.07593%)
Skipped 1316 of 1317 files with non-matching indexes
Il y a toujours une certaine variation dans le temps écoulé, 0,0487 seconde étant le meilleur temps des quatre cycles de recherche qui ont produit une plage de temps de recherche de 0,0487 (accélération 21x) à 0,0983 secondes (accélération 10x).
L'augmentation de la vitesse peut être significativement plus élevée en général par rapport à cette petite démo, en fonction de plusieurs facteurs, de la taille des fichiers indexés, de la vitesse de lecture du système de fichiers et en supposant que la plupart des fichiers sont froids.
L'algorithme d'indexation que j'ai conçu est prouvablement monotone : une précision plus élevée garantit des performances de recherche accrues en réduisant le taux de faux positifs, mais augmente également la surcharge de stockage de l'index. De même, une précision moindre diminue les performances de recherche, mais réduit également la surcharge de stockage de l'index. Par conséquent, j'ai nommé mon indexeur un indexeur monotone .
Si l'espace de stockage de fichiers est limité, nous pouvons alors réduire la surcharge de stockage d'index en spécifiant une précision d'indexation inférieure.
L'indexation de l'exemple ci-dessus avec le niveau 0 (option -0
) réduit la surcharge de stockage d'indexation de 8,6 fois, de 4 256 octets par fichier à un maigre 490 octets par fichier :
12247077 bytes scanned and indexed with 42% noise on average
1317 files indexed in 28 directories
0 new directories indexed
1317 new files indexed
0 modified files indexed
0 deleted files removed from indexes
128 binary files ignored with --ignore-binary
0 symbolic links skipped
0 devices skipped
646123 bytes indexing storage increase at 490 bytes/file
La recherche indexée est toujours beaucoup plus rapide (12 fois) que la recherche non indexée pour cet exemple, avec 16 fichiers réellement recherchés (15 faux positifs) :
Searched 1317 files in 28 directories in 0.0722 seconds with 8 threads: 1 matching (0.07593%)
Skipped 1301 of 1317 files with non-matching indexes
Les modèles Regex plus complexes que cet exemple peuvent naturellement avoir un taux de faux positifs plus élevé, qui correspond au taux de fichiers considérés comme pouvant correspondre alors qu'ils ne le sont pas. Un taux de faux positifs plus élevé peut réduire les vitesses de recherche lorsque le taux est suffisamment élevé pour avoir un impact.
Le tableau suivant montre comment la précision de l'indexation affecte le stockage de l'indexation et le bruit moyen par fichier indexé. Les colonnes les plus à droite indiquent la vitesse de recherche et le taux de faux positifs pour ugrep --index -I -l 'std::chrono'
:
acc. | stockage d'index (Ko) | bruit moyen | faux positifs | temps de recherche (s) |
---|---|---|---|---|
-0 | 631 | 42% | 15 | 0,0722 |
-1 | 1276 | 39% | 1 | 0,0506 |
-2 | 1576 | 36% | 0 | 0,0487 |
-3 | 2692 | 31% | 0 | unch |
-4 | 2966 | 28% | 0 | unch |
-5 | 4953 | 23% | 0 | unch |
-6 | 5474 | 19% | 0 | unch |
-7 | 9513 | 15% | 0 | unch |
-8 | 10889 | 11% | 0 | unch |
-9 | 13388 | 7% | 0 | unch |
Si l'expression régulière spécifiée correspond à beaucoup plus de modèles possibles, par exemple avec la recherche ugrep --index -I -l '(todo|TODO)[: ]'
, alors nous pouvons observer un taux plus élevé de faux positifs parmi les 1317 fichiers recherchés, ce qui entraîne des temps de recherche légèrement plus longs :
acc. | faux positifs | temps de recherche (s) |
---|---|---|
-0 | 189 | 0,292 |
-1 | 69 | 0,122 |
-2 | 43 | 0,103 |
-3 | 19 | 0,101 |
-4 | 16 | 0,097 |
-5 | 2 | 0,096 |
-6 | 1 | unch |
-7 | 0 | unch |
-8 | 0 | unch |
-9 | 0 | unch |
La précision -4
est la valeur par défaut (contre -5
précédemment dans les anciennes versions), ce qui a tendance à très bien fonctionner pour rechercher avec des modèles d'expressions régulières de complexité modeste.
Un mot d'avertissement. Il y a toujours un petit peu de temps système pour vérifier les index. Cela signifie que si tous les fichiers sont déjà mis en cache dans la RAM, parce que les fichiers ont été recherchés ou lus récemment, l'indexation n'accélérera évidemment pas nécessairement la recherche. Dans ce cas, une recherche non indexée pourrait être plus rapide. De plus, une recherche basée sur un index a un temps de démarrage plus long. Ce temps de démarrage augmente lorsque des classes de caractères Unicode et des caractères génériques sont utilisés et doivent être convertis en tables de hachage.
Pour résumer, la recherche basée sur l'index est plus efficace lors de la recherche d'un grand nombre de fichiers froids et lorsque les modèles d'expression régulière ne correspondent pas trop, c'est-à-dire que nous voulons limiter l'utilisation de répétitions illimitées *
et +
et limiter l'utilisation de classes de caractères Unicode lorsque possible. Cela réduit le temps de démarrage de l'ugrep et limite le taux de correspondances de modèles faussement positifs (voir également les questions et réponses ci-dessous).
Indexez de manière récursive et incrémentielle tous les fichiers non binaires affichant la progression :
ugrep-indexer -I -v
Indexez de manière récursive et incrémentielle tous les fichiers non binaires, y compris les fichiers non binaires stockés dans des archives et dans des fichiers compressés, en affichant la progression :
ugrep-indexer -z -I -v
Indexez incrémentalement tous les fichiers non binaires, y compris les archives et les fichiers compressés, affichez la progression, suivez les liens symboliques vers les fichiers (mais pas vers les répertoires), mais n'indexez pas les fichiers et répertoires correspondant aux globs dans .gitignore :
ugrep-indexer -z -I -v -S -X
Forcez la réindexation de tous les fichiers non binaires, y compris les archives et les fichiers compressés, suivez les liens symboliques vers les fichiers (mais pas vers les répertoires), mais n'indexez pas les fichiers et répertoires correspondant aux globs dans .gitignore :
ugrep-indexer -f -z -I -v -S -X
Idem, mais réduisez au minimum le stockage des fichiers d'index en diminuant la précision de l'indexation de 5 (par défaut) à 0 :
ugrep-indexer -f -0 -z -I -v -S -X
Augmentez les performances de recherche en augmentant la précision de l'indexation de 5 (par défaut) à 7 au prix de fichiers d'index plus volumineux :
ugrep-indexer -f7zIvSX
Supprimez de manière récursive tous les fichiers d'index ._UG#_Store
masqués pour restaurer l'arborescence des répertoires en mode non indexé :
ugrep-indexer -d
Configurez et compilez avec :
./build.sh
Si vous le souhaitez mais pas obligatoire, installez avec :
sudo make install
Ajoutez une option pour créer un fichier d'index, par exemple spécifié explicitement à ugrep. Cela pourrait encore améliorer la vitesse de recherche indexée si le fichier d'index se trouve sur un système de fichiers rapide. Sinon, ne vous attendez pas à une grande amélioration ni même à un éventuel ralentissement, puisqu'un seul fichier d'index ne peut pas être recherché simultanément et que davantage d'entrées d'index seront vérifiées alors qu'en fait les répertoires sont ignorés (en sautant également leurs index). Les expériences le diront. Un inconvénient majeur de cette approche est que la recherche basée sur l'index avec ugrep --index
n'est plus sûre : les fichiers nouveaux et modifiés qui ne sont pas encore indexés ne seront pas recherchés.
Chaque filtre N-gram Bloom possède son propre « niveau de bits » dans la table de hachage pour éviter les conflits de hachage. Par exemple, 2 grammes ne partagent aucun bit avec 3 grammes. Cela garantit que nous n’aurons jamais de faux positifs avec des caractères faussement correspondants qui ne font en réalité pas partie du modèle. Cependant, l'espace binaire d'un gramme (caractère unique) est petit (au maximum 256 bits). Par conséquent, nous gaspillons certains bits lorsque les tables de hachage sont plus grandes. Une approche possible pour réduire les déchets consiste à combiner 1 gramme avec 2 grammes pour partager le même espace binaire. C'est facile à faire si l'on considère qu'un 1 gramme est égal à un 2 grammes avec le deuxième caractère défini sur