La utilidad ugrep-indexer indexa archivos de forma recursiva para acelerar el grepping recursivo.
Además, el contenido de los archivos comprimidos y comprimidos se indexa cuando se especifica con una opción de línea de comandos. Esto elimina la búsqueda cuando ninguno de sus contenidos coincide con los patrones especificados.
ugrep es un buscador rápido de archivos compatible con grep que admite búsquedas basadas en índices. La búsqueda basada en índices puede ser significativamente más rápida en sistemas de archivos lentos y cuando el almacenamiento en caché del sistema de archivos es ineficaz: si el sistema de archivos en una unidad buscada no está almacenado en caché en la RAM, es decir, está "frío", entonces la indexación acelerará la búsqueda. Solo busca aquellos archivos que puedan coincidir con un patrón de expresiones regulares específico utilizando un índice del archivo. Este índice permite comprobar rápidamente si existe una posible coincidencia, así evitamos buscar en todos los archivos.
La búsqueda indexada con ugrep es segura y nunca omite archivos actualizados que ahora pueden coincidir. Si se agregan o modifican archivos y directorios después de la indexación, la búsqueda siempre buscará estas adiciones y cambios realizados en el sistema de archivos comparando las marcas de tiempo de archivos y directorios con la marca de tiempo de indexación.
Cuando se agregan o modifican muchos archivos después de la indexación, es posible que deseemos volver a indexar para actualizar los índices. La reindexación es incremental, por lo que no llevará tanto tiempo como el proceso de indexación inicial.
Un ejemplo típico pero pequeño de una búsqueda basada en índices, por ejemplo en el repositorio ugrep v3.12.6 ubicado en una unidad separada:
$ 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 búsqueda normal en un sistema de archivos frío sin indexación tarda 1,02 segundos después de desmontar la drive
y volver a montarla para borrar la caché de FS y registrar el efecto de la indexación:
$ 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 tarda más con 1,18 segundos para la misma búsqueda en frío (ripgrep omite archivos binarios de forma predeterminada, por lo que la opción -I
no está especificada):
$ time rg -l 'std::chrono'
src/ugrep.cpp
1.18 real 0.01 user 0.06 sys
Por el contrario, con la indexación, la búsqueda en un sistema de archivos frío solo toma 0,0487 segundos con ugrep, que es 21 veces más rápido, después de desmontar drive
y volver a montarla para borrar la caché de FS y registrar el efecto de la indexación:
$ 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
Siempre hay cierta variación en el tiempo transcurrido, siendo 0,0487 segundos el mejor tiempo de cuatro ejecuciones de búsqueda que produjeron un rango de tiempo de búsqueda de 0,0487 (aceleración de 21 veces) a 0,0983 segundos (aceleración de 10 veces).
El aumento de velocidad puede ser significativamente mayor en general en comparación con esta pequeña demostración, dependiendo de varios factores, el tamaño de los archivos indexados, la velocidad de lectura del sistema de archivos y suponiendo que la mayoría de los archivos estén fríos.
El algoritmo de indexación que diseñé es probablemente monótono : una mayor precisión garantiza un mayor rendimiento de la búsqueda al reducir la tasa de falsos positivos, pero también aumenta la sobrecarga de almacenamiento del índice. Del mismo modo, una menor precisión disminuye el rendimiento de la búsqueda, pero también reduce la sobrecarga de almacenamiento del índice. Por lo tanto, llamé a mi indexador indexador monótono .
Si el espacio de almacenamiento de archivos es escaso, entonces podemos reducir la sobrecarga de almacenamiento de índice especificando una precisión de indexación más baja.
Indexar el ejemplo anterior con el nivel 0 (opción -0
) reduce la sobrecarga de almacenamiento de indexación 8,6 veces, de 4256 bytes por archivo a unos miserables 490 bytes por archivo:
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 búsqueda indexada sigue siendo 12 veces más rápida que la no indexada en este ejemplo, con 16 archivos realmente buscados (15 falsos positivos):
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
Los patrones de expresiones regulares que son más complejos que este ejemplo pueden tener naturalmente una tasa de falsos positivos más alta, que es la tasa de archivos que se consideran posiblemente coincidentes cuando no lo son. Una tasa de falsos positivos más alta puede reducir la velocidad de búsqueda cuando la tasa es lo suficientemente grande como para tener impacto.
La siguiente tabla muestra cómo la precisión de la indexación afecta el almacenamiento de indexación y el ruido promedio por archivo indexado. Las columnas de la derecha muestran la velocidad de búsqueda y la tasa de falsos positivos para ugrep --index -I -l 'std::chrono'
:
según | almacenamiento de índice (KB) | ruido promedio | falsos positivos | tiempo de búsqueda (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 la expresión regular especificada coincide con muchos más patrones posibles, por ejemplo con la búsqueda ugrep --index -I -l '(todo|TODO)[: ]'
, entonces podemos observar una mayor tasa de falsos positivos entre los 1317 archivos buscados. lo que resulta en tiempos de búsqueda ligeramente más largos:
según | falsos positivos | tiempo de búsqueda (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 precisión -4
es la predeterminada (antes -5
en versiones anteriores), que tiende a funcionar muy bien para buscar con patrones de expresiones regulares de complejidad modesta.
Una palabra de precaución. Siempre hay una pequeña sobrecarga para verificar los índices. Esto significa que si todos los archivos ya están almacenados en caché en la RAM, porque los archivos fueron buscados o leídos recientemente, entonces la indexación no necesariamente acelerará la búsqueda, obviamente. En ese caso, una búsqueda no indexada podría ser más rápida. Además, una búsqueda basada en índices tiene un tiempo de inicio más largo. Este tiempo de inicio aumenta cuando se utilizan clases de caracteres Unicode y comodines que deben convertirse en tablas hash.
En resumen, la búsqueda basada en índices es más eficaz cuando se buscan muchos archivos fríos y cuando los patrones de expresiones regulares no coinciden demasiado, es decir, queremos limitar el uso de repeticiones ilimitadas *
y +
y limitar el uso de clases de caracteres Unicode cuando posible. Esto reduce el tiempo de inicio de ugrep y limita la tasa de coincidencias de patrones falsos positivos (consulte también las preguntas y respuestas a continuación).
Indexe de forma recursiva e incremental todos los archivos no binarios que muestren progreso:
ugrep-indexer -I -v
Indexe de forma recursiva e incremental todos los archivos no binarios, incluidos los archivos no binarios almacenados en archivos comprimidos y en archivos comprimidos, mostrando el progreso:
ugrep-indexer -z -I -v
Indexe incrementalmente todos los archivos no binarios, incluidos archivos y archivos comprimidos, muestre el progreso, siga enlaces simbólicos a archivos (pero no a directorios), pero no indexe archivos ni directorios que coincidan con los globos en .gitignore:
ugrep-indexer -z -I -v -S -X
Fuerce la reindexación de todos los archivos no binarios, incluidos archivos y archivos comprimidos, siga enlaces simbólicos a archivos (pero no a directorios), pero no indexe archivos ni directorios que coincidan con los globos en .gitignore:
ugrep-indexer -f -z -I -v -S -X
Lo mismo, pero reduzca el almacenamiento de archivos de índice al mínimo disminuyendo la precisión de la indexación de 5 (predeterminado) a 0:
ugrep-indexer -f -0 -z -I -v -S -X
Aumente el rendimiento de la búsqueda aumentando la precisión de la indexación de 5 (predeterminado) a 7 a costa de archivos de índice más grandes:
ugrep-indexer -f7zIvSX
Elimine recursivamente todos los archivos de índice ._UG#_Store
ocultos para restaurar el árbol de directorios a no indexado:
ugrep-indexer -d
Configurar y compilar con:
./build.sh
Si lo desea pero no es necesario, instale con:
sudo make install
Agregue una opción para crear un archivo de índice, por ejemplo, especificado explícitamente en ugrep. Esto podría mejorar aún más la velocidad de búsqueda indexada si el archivo indexado se encuentra en un sistema de archivos rápido. De lo contrario, no espere muchas mejoras o incluso una posible desaceleración, ya que no se puede buscar un único archivo de índice simultáneamente y se verificarán más entradas de índice cuando en realidad se omiten directorios (omitiendo también sus índices). Los experimentos lo dirán. Una advertencia fundamental de este enfoque es que la búsqueda basada en índices con ugrep --index
ya no es segura: no se buscarán archivos nuevos y modificados que aún no estén indexados.
Cada filtro Bloom de N-gram tiene su propio "nivel de bits" en la tabla hash para evitar conflictos hash. Por ejemplo, 2 gramos no comparten ningún bit con 3 gramos. Esto garantiza que nunca tengamos falsos positivos con caracteres que coincidan falsamente y que en realidad no sean parte del patrón. Sin embargo, el espacio de bits de 1 gramo (un solo carácter) es pequeño (como máximo 256 bits). Por lo tanto, desperdiciamos algunos bits cuando las tablas hash son más grandes. Un posible enfoque para reducir el desperdicio es combinar 1 gramo con 2 gramos para compartir el mismo espacio de bits. Esto es fácil de hacer si consideramos que 1 gramo es igual a 2 gramos con el segundo carácter establecido en