5 puntos por GN⁺ 2023-12-01 | 1 comentarios | Compartir por WhatsApp
  • ripgrep(rg) es una herramienta de búsqueda en línea de comandos basada en Rust que combina la comodidad de búsqueda de código al estilo de The Silver Searcher con el rendimiento bruto de GNU grep, y ofrece binarios para Linux, Mac y Windows
  • En 25 benchmarks, no hubo ninguna herramienta que superara claramente a ripgrep tanto en rendimiento como en precisión, ni en búsquedas sobre un solo archivo grande ni en directorios masivos, y el costo del soporte para Unicode también se mantuvo bajo
  • Amplía el alcance práctico de las herramientas de búsqueda de código al incluir manejo de .gitignore, exclusión predeterminada de archivos ocultos y binarios, filtros por tipo de archivo, soporte opcional para PCRE2, búsqueda en múltiples codificaciones y archivos comprimidos, e incluso filtros de preprocesamiento
  • La diferencia entre los experimentos con el repositorio del kernel de Linux y OpenSubtitles2016 depende en gran medida de la optimización de literales, la búsqueda multipatrón Teddy SIMD, Aho-Corasick, el método de decodificación UTF-8, el conteo de líneas y el costo de procesar .gitignore
  • Al buscar en paralelo muchos archivos pequeños, el mapeo de memoria puede volverse más lento, mientras que en un solo archivo grande puede ser ventajoso; por eso ripgrep usa según el caso búsqueda con búfer intermedio o búsqueda con mapeo de memoria

La posición a la que apuntaba ripgrep

  • ripgrep es una herramienta de búsqueda en línea de comandos que busca combinar la comodidad de las herramientas de búsqueda de código con el rendimiento de las herramientas tipo grep
  • Los objetos de comparación son GNU grep, git grep, The Silver Searcher(ag), Universal Code Grep(ucg), The Platinum Searcher(pt) y sift
  • Los benchmarks buscaban confirmar tres puntos clave
    • No hay ninguna herramienta claramente superior a ripgrep tanto en búsquedas sobre un solo archivo como en búsquedas sobre directorios de gran tamaño
    • Ofrece soporte para Unicode correcto sin exigir un gran costo de rendimiento
    • Al buscar varios archivos a la vez, el mapeo de memoria puede ser, por lo general, más lento en lugar de más rápido
  • El autor es quien creó ripgrep y el motor de expresiones regulares en el que se basa, y aclara que los benchmarks pueden estar seleccionados de forma sesgada

Funciones y comportamiento predeterminado

  • El nombre del ejecutable de ripgrep es rg
  • La búsqueda predeterminada recorre recursivamente el directorio actual, respeta .gitignore y omite archivos ocultos y binarios
  • También soporta .rgignore, y los patrones de .rgignore tienen prioridad sobre los de .gitignore
  • Con -u, -uu y -uuu se puede ampliar el alcance para ignorar archivos de ignore, incluir archivos ocultos e incluir archivos binarios
    • rg -uuu es similar a grep -a -r
  • Soporta filtros por tipo de archivo
    • rg -tpy foo: busca solo en archivos Python
    • rg -Tjs foo: excluye archivos JavaScript
    • Se pueden agregar nuevas reglas de tipo de archivo con --type-add
  • También ofrece varias funciones de grep
    • salida con contexto
    • búsqueda de múltiples patrones
    • resaltado con color
    • soporte completo para Unicode
  • El motor de expresiones regulares predeterminado no soporta look-around ni backreference, pero al elegir el motor PCRE2 con -P se pueden usar esas funciones
  • También soporta detección automática parcial de UTF-16 y especificación de codificación mediante -E/--encoding
    • Incluye UTF-16, latin-1, GBK, EUC-JP, Shift_JIS y otras
  • Con -z/--search-zip permite buscar dentro de archivos comprimidos como gzip, xz, lzma, bzip2 y lz4
  • También admite filtros arbitrarios de preprocesamiento como extracción de texto de PDF, descompresión adicional, descifrado y detección automática de codificación

Razones para no usarlo

  • Si la máxima prioridad es la portabilidad y poder usarlo en cualquier parte, grep, al ser estándar y estar ampliamente instalado, puede ser la mejor opción
  • Si dependes de alguna función o bug específico presente en otras herramientas, puede que ripgrep no sea adecuado
  • En algunos casos límite de rendimiento, otras herramientas pueden comportarse mejor
  • Si no se puede instalar o no hay soporte para la plataforma, tampoco se podrá usar

Estructura de funcionamiento de las herramientas tipo grep

  • Las herramientas de búsqueda pasan, en general, por tres etapas
    • recopilar los archivos a buscar
    • realizar la búsqueda real
    • mostrar los resultados
  • Las herramientas tipo grep deben buscar bien en archivos grandes, por lo que el rendimiento del motor de expresiones regulares es importante
  • Las herramientas tipo ack deben procesar rápido el recorrido recursivo de directorios y la aplicación de reglas de ignore como .gitignore
  • ripgrep intenta combinar ambos enfoques
    • motor de expresiones regulares rápido
    • búsqueda en paralelo
    • filtrado de objetivos de búsqueda

Recolección de archivos y manejo de ignore

  • En las herramientas tipo ack, es importante decidir rápidamente qué archivos buscar desde el directorio actual
  • El rendimiento del recorrido de directorios se ve afectado por la cantidad de llamadas stat innecesarias
  • ripgrep usa un iterador recursivo de directorios orientado a minimizar las llamadas al sistema
  • Procesar .gitignore tiene un costo
    • hay que encontrar archivos de ignore en cada directorio
    • hay que compilar los patrones de ignore
    • hay que aplicar los patrones a todas las rutas candidatas
  • El repositorio del kernel de Linux tenía 4,640 directorios y 178 archivos .gitignore
  • ripgrep intenta soportar de forma más completa la semántica de .gitignore y da prioridad al patrón coincidente definido más recientemente
  • ucg puede ser más rápido porque usa reglas glob basadas en listas blancas en lugar de .gitignore, pero puede pasar por alto archivos con extensiones desconocidas

Diferencias entre motores de expresiones regulares

  • Los motores de expresiones regulares se dividen, en general, en dos grupos
    • basados en backtracking: tienen muchas funciones, pero pueden volverse exponencialmente lentos con algunas entradas
    • basados en autómatas finitos: pueden tener funciones más limitadas, pero garantizan tiempo lineal respecto a la longitud del texto de búsqueda
  • Los motores por herramienta son los siguientes
    • GNU grep, git grep: motores propios basados en autómatas finitos
    • ripgrep: biblioteca Rust regex, basada en autómatas finitos
    • ag, ucg: basados en PCRE con backtracking
    • pt, sift: biblioteca Go regex, basada en autómatas finitos
  • ag y ucg, al usar PCRE, pueden quedar expuestos al peor comportamiento del backtracking
  • El patrón de ejemplo (a*)* c puede causar problemas en herramientas basadas en PCRE, pero las demás herramientas evaluadas en los benchmarks lo procesan sin problemas

Optimización de literales y SIMD

  • En búsquedas de cadenas simples, la optimización de búsqueda de literales puede ser más importante que el motor de expresiones regulares
  • Boyer-Moore es un algoritmo clásico de búsqueda de subcadenas, y puede usar rutinas como memchr para encontrar rápidamente posiciones candidatas
  • Las implementaciones de memchr a menudo inspeccionan 16 bytes a la vez con instrucciones SIMD, y pueden alcanzar un rendimiento de varios GB/s
  • La biblioteca Rust regex extrae activamente literales de prefijo y sufijo del patrón
    • foo|bar
    • (a|b)c
    • [ab]foo[yz]
    • (foo)?bar
    • (foo)*bar
    • (foo){3,6}
  • Si toda la expresión regular puede descomponerse en un solo literal o en una alternancia de literales, puede incluso no usar en absoluto el motor principal de expresiones regulares
  • ripgrep también extrae literales internos aprovechando que muestra resultados por línea
    • Ejemplo: en \w+foo\d+, primero encuentra foo y luego valida con la expresión regular solo las líneas candidatas
  • Para buscar múltiples literales, GNU grep usa un algoritmo similar a Commentz-Walter, mientras que Rust regex usa Aho-Corasick o el algoritmo Teddy SIMD
  • Teddy es un algoritmo de búsqueda multipatrón basado en SIMD proveniente de Intel Hyperscan, y es una de las optimizaciones clave por las que ripgrep supera a GNU grep

Método de búsqueda: evitar la búsqueda línea por línea

  • Una implementación ingenua lee el archivo línea por línea y aplica el patrón a cada línea, pero como en la mayoría de las búsquedas las coincidencias son escasas, eso resulta ineficiente
  • Las herramientas de búsqueda suelen buscar sobre grandes búferes de bytes de una sola vez
    • mapear el archivo en memoria
    • leer el archivo completo en memoria
    • búsqueda progresiva con un búfer intermedio de tamaño fijo
  • ripgrep, GNU grep y git grep soportan búsqueda progresiva, por lo que puede aplicarse tanto a archivos como a streams
  • La búsqueda progresiva es difícil de implementar
    • cálculo del número de línea
    • manejo de casos en que el búfer termina a mitad de línea
    • manejo de líneas largas
    • manejo de invert match
    • manejo de la salida de contexto alrededor de las coincidencias
  • ripgrep asume la complejidad de implementación y usa búsqueda progresiva, y en los benchmarks muestra resultados más rápidos que el mapeo de memoria al buscar muchos archivos pequeños

Salida y paralelismo

  • En la búsqueda en paralelo, si cada hilo imprime de inmediato, los resultados de distintos archivos pueden mezclarse
  • Todas las herramientas de búsqueda de código en paralelo escriben primero los resultados en un búfer intermedio en memoria y serializan solo la etapa de salida
  • Este enfoque permite que los hilos de búsqueda realicen la búsqueda real en paralelo
  • La desventaja es que el uso de memoria puede crecer, por ejemplo, con un archivo de 2 GB donde todas las líneas hacen match
  • ripgrep escribe directamente a stdout sin búfer intermedio cuando busca en stdin o en un solo archivo

Metodología de benchmarks

  • Los benchmarks se dividen según el problema del usuario final
    • búsqueda en repositorios de código grandes
    • búsqueda en un solo archivo grande
  • Los patrones de búsqueda están sesgados hacia literales simples, alternation y expresiones regulares ligeras
  • Como el comportamiento predeterminado varía entre herramientas, se intentó igualar condiciones como números de línea, Unicode, .gitignore y listas blancas para hacer comparaciones justas
  • Las versiones evaluadas en los benchmarks fueron las siguientes
    • ripgrep v0.1.2
    • GNU grep v2.25
    • git grep v2.7.4
    • ag commit cda635, PCRE 8.38
    • ucg commit 487bfb, PCRE 10.21 JIT
    • pt commit 509368
    • sift commit 2d175c
  • ack quedó fuera porque en ese momento era mucho más lento que las otras herramientas
  • El runner de benchmarks es benchsuite, que requiere Python 3.5 o superior, y está incluido en el repositorio de ripgrep
  • Antes de medir, cada comando ejecuta 3 warm-ups para que el corpus quede cargado en el page cache del sistema operativo
  • Cada comando se mide 10 veces y se registran el promedio y la desviación estándar
  • El entorno de ejecución fue Amazon EC2 c3.2xlarge, Ubuntu 16.04, Xeon E5-2680 2.8GHz, 16 GB de memoria y SSD de 80 GB
  • También se publicaron el log de configuración, los resultados resumidos y el CSV en bruto

Resultados de búsqueda en el código del kernel de Linux

  • El benchmark de búsqueda en código se ejecutó sobre un repositorio del kernel de Linux compilado, commit d0acc7
  • La razón para usar un repositorio del kernel compilado es que los artefactos de compilación pueden permanecer en el repositorio y afectar tanto la relevancia de los resultados como el rendimiento
  • En linux_literal_default, la búsqueda del literal simple PM_RESUME muestra las diferencias de comportamiento predeterminado entre las herramientas
    • rg respeta .gitignore y omite archivos ocultos y binarios
    • ag y pt son similares, pero cuentan el número de líneas
    • ucg no lee .gitignore y busca con base en una lista blanca
    • sift busca casi todo por defecto
    • git grep tiene la ventaja de obtener el conjunto de archivos a buscar desde el índice de git
  • Respetar .gitignore mejora la relevancia de los resultados, pero puede tener un costo en rendimiento
  • En linux_literal, rg (whitelist) mostró un rendimiento casi igual al de ucg, y rg (ignore) quedó en un nivel similar al de git grep
  • rg (ignore) (mmap) y ag (ignore) (mmap) fueron más lentos por usar memory map, y en las mismas condiciones rg (ignore) fue mucho más rápido
  • En una máquina local, las versiones con memory map también fueron más lentas, aunque la diferencia fue menor que en EC2

Unicode y búsqueda sin distinguir mayúsculas y minúsculas

  • En linux_literal_casei, pt se volvió mucho más lento al procesar -i como (?i) del regexp de Go
  • sift se volvió menos lento porque convierte el patrón y el bloque de búsqueda a minúsculas, pero esa optimización solo maneja mayúsculas y minúsculas ASCII y no es correcta para Unicode
  • ripgrep convierte la búsqueda case-insensitive en combinaciones literales cuando es posible y usa Teddy para encontrar rápidamente posiciones candidatas
  • La búsqueda \wAh en linux_unicode_word verifica si \w con soporte Unicode encuentra resultados como µAh
  • Solo rg y git grep podían activar o desactivar Unicode; ag, pt, sift y ucg usan \w solo para ASCII
  • git grep pagó un costo grande de rendimiento al activar soporte Unicode, mientras que ripgrep casi no tuvo degradación
  • ripgrep incorpora la decodificación UTF-8 en la máquina de estados finitos y hace match directamente sobre cadenas de bytes UTF-8, sin una etapa separada de decodificación

Diferencias según la complejidad de la expresión regular

  • En regex con sufijo literal como [A-Z]+_RESUME, rg y ucg usan _RESUME para encontrar rápidamente candidatos
  • En alternation de literales como ERR_SYS|PME_TURN_OFF|LINK_REQ_RST|CFG_BME_EVT, ripgrep usa Teddy y puede incluso evitar por completo el motor principal de regex
  • Incluso en alternation case-insensitive, ripgrep crea prefijos con combinaciones de mayúsculas y minúsculas para encontrar candidatos con Teddy, y valida solo esos candidatos con la regex completa
  • En la búsqueda \p{Greek}, solo Rust regex y Go regex soportaban esa propiedad Unicode, y rg fue mucho más rápido que pt y sift
  • En la búsqueda case-insensitive de \p{Greek}, sift no pudo reportar matches y pt no manejó correctamente mayúsculas y minúsculas Unicode
  • En patrones sin literales, como \w{5}\s+..., el rendimiento del motor de regex se ve directamente
    • rg siguió siendo rápido incluso con soporte Unicode activado
    • git grep paga un costo grande con soporte Unicode
    • Un DFA Unicode maneja un conjunto de estados NFA mucho mayor que un DFA ASCII; en el ejemplo, unas 250 NFA para ASCII frente a unas 77,000 para Unicode

Búsqueda en un solo archivo grande

  • El benchmark de archivo único usó una muestra de OpenSubtitles2016
    • la muestra en inglés es de aproximadamente 1 GB
    • la muestra en ruso es de aproximadamente 1.6 GB
  • En esta área, el rendimiento del motor de regex y las optimizaciones para literales se vuelven más importantes
  • En subtitles_literal, tanto la búsqueda de Sherlock Holmes como la de Шерлок Холмс fueron más rápidas con rg
  • ripgrep intenta elegir un byte poco frecuente en un literal para usarlo con memchr
    • Una implementación estándar de Boyer-Moore suele usar el último byte para buscar candidatos
    • rg elige un byte más raro para poder saltar más tiempo dentro del loop optimizado con SIMD
  • En UTF-8, muchos caracteres del patrón en ruso empiezan con \xD0 o \xD1, así que buscar el primer byte puede ser ineficiente
  • rg usa una tabla precalculada de frecuencias de 256 bytes para preferir bytes menos frecuentes en lugar de \xD0 o \xD1
  • En un solo archivo grande, como solo hay que crear el memory map una vez, la búsqueda con memory map de rg fue alrededor de 25% más rápida que rg (no mmap)

Unicode y alternation en archivo único

  • En subtitles_literal_casei, rg maneja correctamente la búsqueda Unicode sin distinguir mayúsculas y minúsculas y aun así es rápido
  • GNU grep paga un costo grande en la búsqueda Unicode case-insensitive
  • En la búsqueda case-insensitive en ruso, grep (ASCII) parece ignorar en la práctica -i, y ag reportó 0 matches
  • En subtitles_alternate, la búsqueda con alternation de varios nombres de personajes fue más rápida con rg tanto en inglés como en ruso
  • En alternation en inglés, rg fue aproximadamente un orden de magnitud más rápido que GNU grep
  • En subtitles_alternate_casei, rg fue mucho más lento que antes, pero en inglés siguió superando a las otras herramientas
  • En este caso, había demasiados candidatos literales para que Teddy los manejara, así que rg cambia de Teddy a Aho-Corasick
  • ripgrep usa un Aho-Corasick “advanced” basado en tablas de transición, que realiza una transición por cada byte de entrada

Inner literal y patrones sin literales

  • Los patrones como \w+\s+Holmes\s+\w+ estaban diseñados para evitar la optimización de literales de prefijo y sufijo, pero aun así podían aprovechar el literal interno Holmes
  • ripgrep y GNU grep realizan optimización de inner literal
  • ripgrep usa regex-syntax de Rust regex para extraer literales del AST del patrón
  • En la versión en ruso \w+\s+Холмс\s+\w+, solo las herramientas con soporte correcto de Unicode pudieron dar resultados realmente útiles
  • En patrones largos sin ningún literal, como \w{5}\s+..., rg estuvo entre los más rápidos en inglés, y la versión de GNU grep con soporte Unicode quedó fuera porque tardó más de 90 segundos en inglés y más de 4 minutos en ruso
  • ripgrep mantiene el soporte Unicode y al mismo tiempo logra buen rendimiento al incorporar la decodificación UTF-8 dentro del DFA

Benchmarks adicionales

  • everything es una prueba poco realista que hace match de todas las líneas con .* en el repositorio de Linux
    • rg reportó 22,065,361 líneas en 1.081 segundos
    • ag y pt no reportaron todas las líneas, así que parece que tienen un límite de coincidencias
  • nothing es una prueba que no reporta ninguna línea al aplicar invert match sobre .*
    • rg registró 0.302 segundos y git grep 0.905 segundos
    • pt y ucg no soportan búsqueda invertida
  • context imprime 2 líneas de contexto alrededor de Sherlock Holmes en el corpus de subtítulos en inglés
    • rg fue similar a sift, con 0.612 segundos frente a 0.717 segundos
    • ucg no soporta esa función
  • huge busca Sherlock Holmes en los 9.3 GB completos de subtítulos en inglés
    • rg marcó 1.786 segundos, GNU grep 5.119 segundos y sift 3.047 segundos
    • ucg dio un resultado incorrecto al reportar solo 1,543 líneas bajo la condición de conteo de líneas, y se sospecha que tuvo problemas al buscar en archivos de más de 2 GB

Conclusión

  • ripgrep no siempre ganó todos los benchmarks en búsquedas dentro del repositorio del kernel de Linux, pero era difícil decir que otra herramienta tuviera una ventaja clara tanto en rendimiento como en precisión
  • git grep podía llevar ventaja por unos pocos milisegundos en algunos casos simples, pero cuando el patrón se volvía más complejo o se requería Unicode, hubo casos en los que ripgrep quedó muy por delante
  • Los siguientes factores contribuyen al rendimiento de ripgrep en búsqueda de código
    • recorrido rápido de directorios, orientado a minimizar las llamadas a stat
    • coincidencia de globs de .gitignore usando RegexSet
    • distribución del trabajo mediante una cola Chase-Lev con work stealing
    • la decisión de no usar memory maps al buscar entre muchos archivos pequeños
    • un motor de expresiones regulares rápido
  • En búsquedas sobre un solo archivo, ripgrep fue el más rápido en todos los benchmarks principales o quedó muy por delante
  • En el rendimiento sobre un solo archivo influyen memchr basado en sparse bytes, Teddy SIMD, Aho-Corasick y un DFA con decodificación UTF-8 incorporada
  • En los benchmarks que requerían funciones Unicode, solo rg, GNU grep y git grep mostraron soporte realmente útil, y GNU grep y git grep por lo general pagaron un costo de rendimiento alto
  • Los memory maps fueron desventajosos en Linux x86_64 para búsquedas paralelas entre muchos archivos pequeños, ventajosos para búsquedas en un solo archivo grande, y en entornos VM podían tener una penalización adicional

1 comentarios

 
GN⁺ 2023-12-01
Opiniones de Hacker News
  • Definitivamente es rápido, y sigo recomendando la combinación con fzf
    Lo uso como una función de PowerShell que primero busca con ripgrep, luego aplica búsqueda difusa sobre los archivos+texto resultantes, y muestra el contexto con bat
    En proyectos donde se mezclan varios repositorios, permite acotar muy rápido cuando “sé que está en algún lado, pero no sé la ubicación exacta ni el nombre”
    Este método viene de https://github.com/junegunn/fzf/blob/master/ADVANCED.md, y aunque no lo uses todo, vale la pena echarle un vistazo para sacar ideas

    • Yendo un paso más allá, recomiendo integrar ripgrep-all(rga) con fzf
      Permite hacer búsqueda difusa no solo en archivos de texto, sino también en varios formatos de archivo como PDF y zip
      Hay más detalles en https://github.com/phiresky/ripgrep-all/wiki/fzf-Integration
    • También escribí esto en una versión para bash
      El enfoque consiste en elegir un resultado de rg con fzf, parsear el archivo y el número de línea seleccionados, y abrirlo con $EDITOR +"${linenumber}" "$file"
    • En Vim, sin fzf+rg siento que casi todo se rompe
      Es como moler café a mano en vez de usar un molinillo eléctrico
    • Con fzf puedes elegir muchos archivos para agregarlos a Git y saltarte algunos
      Si agregas fza = "!git ls-files -m -o --exclude-standard | fzf -m --print0 | xargs -0 git add" en [alias] de gitconfig, git fza muestra la lista de archivos modificados o aún no agregados, y puedes alternar elementos con espacio mientras avanzas
      Este alias y fzf+fd aceleran bastante parte del flujo de trabajo
      También hay una guía que recopila cosas para poner en la configuración de zsh en macOS: https://gist.github.com/aclarknexient/0ffcb98aa262c585c49d4b...
    • Yo también uso ripgrep casi de la misma forma
      Lo uso como punto de partida para acotar archivos o proyectos en una base de código con cientos de repositorios, y luego profundizo más
  • En Emacs uso ripgrep con los paquetes project.el y dumb-jump
    Quizá no sea la forma más popular, pero la experiencia completa es bastante satisfactoria
    Basta con instalar dumb-jump con package-install y configurar (add-hook 'xref-backend-functions #'dumb-jump-xref-activate)
    En proyectos Python, al buscar la definición de un identificador con M-. o C-u M-., dumb-jump ejecuta un comando rg adecuado para el proyecto y el tipo de archivo actuales, y muestra los resultados en el buffer de Xref
    También soporta ag, y si no están ag ni rg, vuelve a grep, aunque al buscar en todo el directorio home puede ser lento, como era de esperarse

    • Incluso solo con project.el, incluido por defecto en Emacs, se puede usar ripgrep con bastante facilidad
      No es estrictamente necesario un paquete externo; si quieres usarlo en lugar del lento grep en directorios grandes, configura (setq xref-search-program 'ripgrep)
      Entonces una búsqueda de proyecto como C-x p g foo RET se ejecuta en el proyecto actual con una forma como rg -i --null -nH --no-heading --no-messages -g '!*/' -e foo
      Los resultados aparecen en el buffer de Xref, lo que facilita moverse al siguiente/anterior match, saltar al código fuente y mostrarlo en una ventana dividida con teclas como n, p, RET, C-o
    • Como autor de ripgrep, aunque no ejecuté directamente esa expresión regular, creo que se puede quitar el flag --pcre2
      También parece que se pueden quitar la segunda y la tercera aserción \b; la primera podría ser necesaria
    • Deadgrep usa ripgrep y también tiene bindings de evil-collection, así que se puede usar de forma satisfactoria: https://github.com/Wilfred/deadgrep
    • Este método también es bueno, pero cuando quiero buscar en varios proyectos a la vez o solo en una subcarpeta dentro de un proyecto, todavía uso rg.el
      En esos casos, antes habría usado rgrep
  • Lo interesante es que la búsqueda de VS Code ahora también funciona con ripgrep a través de un wrapper de Node.js
    https://www.npmjs.com/package/@vscode/ripgrep

    • Esto es muy útil si estás en un entorno donde puedes solicitar o instalar VS Code, pero no ripgrep
      Puedes encontrar el binario rg dentro de la ruta de instalación de VS. Al menos en mi entorno laboral de Windows se podía
    • Siempre me pregunté por qué la búsqueda era tan rápida si VS Code es una app Electron; ahora entiendo la razón
    • No es una función nueva: está en VS Code desde hace 7 años
  • He usado ripgrep durante unos 2 años y ya se volvió una herramienta imprescindible
    La razón principal por la que dejé grep fue la facilidad de uso
    Como por defecto respeta las reglas de .gitignore y omite archivos/directorios ocultos y archivos binarios, rg search_term directory es mucho mejor que el comando equivalente con grep, y la mejora de velocidad viene de regalo
    Cuando un match es demasiado largo y deja la terminal hecha un desastre, uso seguido la opción -M, como -M 1000

    • -M es realmente excelente
      Es especialmente cómoda para ignorar resultados de archivos minificados que no quieres ver, y también es buena la opción -g para buscar solo archivos con una extensión específica, como -g *.cs
      También es útil que sea un binario independiente y portable: cuando trabajo en una máquina nueva, pongo el ejecutable y configuro el alias de grep a rg, así aunque escriba grep por costumbre, se ejecuta rg
  • Incluso en 2023 puede seguir siendo cierto, pero el problema es que las herramientas paralelizadas que reemplazan a grep, por ejemplo ripgrep o ag, son tan rápidas frente al grep tradicional que las pequeñas diferencias de velocidad entre ellas difícilmente sirven como criterio de diferenciación.
    Uso ag dentro de Emacs en una base de código de 900 mil líneas, y en un Ryzen Threadripper 2950X de 16 núcleos termina prácticamente al instante.
    No siento la necesidad de reducir algo de menos de 1 segundo a “un poco más de menos de 1 segundo”.
    El atributo clave de las nuevas herramientas tipo grep no es la velocidad; hay que evaluarlas y compararlas de otras maneras.

    • Creo que en 2016 la velocidad sí era claramente el atributo clave.
      ag tiene caídas de rendimiento bastante grandes, y eso también se ve en el post del blog.
      Dicho eso, las cargas de trabajo varían según la persona, así que en algunos casos la diferencia de rendimiento puede no importar.
      900 mil líneas no es tanto, y si la consulta es simple, la mayoría de las herramientas tipo grep que no sean ingenuas la procesan muy rápido.
      Si miramos otros criterios de comparación, ag está prácticamente en soporte vital, y parece que casi fue eliminado de Debian hasta que alguien lo rescató: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=999962
      El post del blog también compara el soporte Unicode, y ag prácticamente no tiene soporte Unicode. Quizá no sea importante para todo el mundo, pero basta como criterio de comparación no relacionado con el rendimiento.
    • En mi experiencia, todas estas herramientas están fuertemente limitadas por el cuello de botella de E/S.
      El tiempo de búsqueda toma lo que tarde el archivo en cargarse desde el disco, y las diferencias posteriores difícilmente son significativas.
      Si los archivos están en caché, el tiempo de moverse por el sistema de archivos y escribir el comando domina más que el tiempo de búsqueda, así que de nuevo es difícil que la diferencia de rendimiento sea significativa.
  • El título necesita (2016).
    Esto es el anuncio original, no información nueva.

  • No es más rápido que qgrep.
    La forma en que funcionan ambos es bastante distinta, y aunque qgrep se basa en re2, su velocidad viene de que tiene un índice.
    En repositorios grandes de archivos, tiene más sentido usar qgrep con un índice que recorrer todos los archivos cada vez; me pregunto por qué la gente olvida la opción de qgrep.
    Eso sí, si se necesita coincidencia multilínea en UTF-8, ripgrep tiene que recurrir a otra biblioteca PCRE2, así que no creo que sea tan rápido.

    • Como autor de ripgrep, es cierto que qgrep, al usar indexación, tiene ventaja frente a herramientas que no indexan.
      A cambio, hay que configurar y mantener el índice, así que la UX no es tan simple como “solo ejecutar la búsqueda”.
      La razón por la que la gente no usa qgrep es parecida a la razón por la que no usa ripgrep: “para mí grep ya es suficientemente rápido”.
      En objetivos de búsqueda pequeños, muchas veces no se percibe la diferencia de velocidad entre ripgrep y grep, o entre qgrep y ripgrep.
      Si ripgrep termina una búsqueda en el kernel de Linux en menos de 100 ms, que valga la pena pasar a una herramienta con indexación para un uso interactivo estándar depende del caso, pero por lo general no.
      He pensado en la idea de agregar indexación a ripgrep: https://github.com/BurntSushi/ripgrep/issues/1497
      Además, la búsqueda multilínea no requiere PCRE2. El motor de expresiones regulares predeterminado también tiene soporte Unicode, y el soporte de búsqueda multilínea se mantiene incluso si se compila sin PCRE2.
  • Después de pasar de ripgrep a ugrep, no he vuelto atrás.
    La velocidad es similar, pero tiene búsqueda difusa, una TUI útil para revisión de código y también puede buscar dentro de PDF y archivos comprimidos.
    También es cómodo poder usar opcionalmente la sintaxis de búsqueda de Google.
    https://ugrep.com

    • Soy fan entusiasta de ripgrep, pero recientemente terminé buscando ugrep por una función que ripgrep no tiene: buscar dentro de archivos zip comprimidos.
      Permite buscar sin descomprimir en disco.
      Trabajo con un corpus comprimido de millones de archivos de texto pequeños, y es bueno no tener que descomprimirlo todo al sistema de archivos. Algunos sistemas de archivos sufren a esa escala.
      Aprecio ambas herramientas, y agradezco a sus respectivos autores.
    • Me da miedo que, si empiezo a usar sintaxis de búsqueda de Google en grep, la mayoría de los resultados intenten venderme algo.
    • Buscando por encima artículos de “ugrep vs ripgrep”, vi publicaciones que parecían mostrar a los autores de ugrep y ripgrep peleándose durante años en Reddit.
      Por ejemplo https://www.reddit.com/r/programming/comments/120wqvr/ripgre...
      Me resulta un poco raro, considerando que solo se trata de herramientas open source.
    • Me pregunto si la TUI es mejor que pasar los resultados a fzf.
      Para mí parece difícil superar la configurabilidad y flexibilidad de fzf.
    • Gracias por mencionarlo.
      Creo que la función estrella es la compatibilidad con las opciones de línea de comandos de grep existentes.
      Está bastante bien no tener que aprender un conjunto de opciones completamente nuevo.
  • Me pregunto por qué grep no se reemplaza ni se mejora
    Creo que este tema ya está algo viejo

    • Hay muchas razones que lo explican
      Inercia, compatibilidad, resistencia al cambio, cosas como el dilema del innovador. No lo digo en sentido negativo; todo eso también aplica a mí
      Sobre compatibilidad, se puede ver el FAQ: https://github.com/BurntSushi/ripgrep/blob/master/FAQ.md#pos...
    • Es parecido a por qué no cambio la silla de 40 años en la que estoy sentado por una Razer UltraSeat XR3000-A
      Es cómoda, encaja bien con mi entorno de trabajo y no hay motivo para cambiarla y volver a ajustar todo
      La analogía solo llega hasta el punto de que ya hay una silla como las de Razer cerca, y está sosteniendo ropa
    • Alguien que diseñó Unix convirtió algunas funciones del sistema en funciones centrales del OS y, al mismo tiempo, en herramientas para personas; como resultado, décadas después aparece una consecuencia rara del tipo “debe existir un programa llamado xyz, debe aceptar estos argumentos y comportarse exactamente así”
    • Ya se pueden usar varias herramientas alternativas como ripgrep
      Si la idea es reemplazar el comando grep en sí por otra utilidad, parece que se romperían demasiadas cosas para el valor que se obtendría
      Quien quiera un grep más rápido puede usar otra herramienta, y quien use el grep existente puede seguir usándolo; así que ya estamos cerca de un estado ideal
    • grep es una herramienta de propósito general para encontrar texto en todo tipo de archivos, y está incrustada en el estándar UNIX
      Algunos programadores la usan para buscar en código fuente, pero otras personas la usan en scripts o para búsquedas de texto que no tienen nada que ver con código fuente, y esperan que nunca crashee
      En cambio, ripgrep es una herramienta especializada y con opiniones fuertes, diseñada principalmente para buscar en repositorios de código fuente
      No hay mucho margen para hacer más rápida la búsqueda de texto de propósito general. Si se usa mmap(), hay riesgo de crashear con archivos truncados; si se reduce la expresividad de las expresiones regulares, puede ser más rápida; y también se podría descartar el soporte para todos los locales y juegos de caracteres y hardcodear solo UTF-8/UTF-16, pero no se debería hacer eso
  • Buscando en Portage, parece que también hay una versión que maneja otros documentos como PDF y doc
    https://github.com/phiresky/ripgrep-all