Ir al contenido

Comandos de hyperfine

hyperfine es una herramienta de benchmarking de línea de comandos escrita en Rust. Proporciona análisis estadístico de tiempos de ejecución de comandos, maneja ejecuciones de calentamiento, soporta benchmarks parametrizados y exporta resultados en múltiples formatos.

Instalación

Linux/Ubuntu

# Ubuntu/Debian
sudo apt install hyperfine

# Fedora
sudo dnf install hyperfine

# Arch Linux
sudo pacman -S hyperfine

# Cargo (Rust)
cargo install hyperfine

# From GitHub releases
wget https://github.com/sharkdp/hyperfine/releases/latest/download/hyperfine_1.19.0_amd64.deb
sudo dpkg -i hyperfine_1.19.0_amd64.deb

# Verify
hyperfine --version

Benchmarking básico

# Benchmark de un solo comando
hyperfine 'sleep 0.5'

# Benchmark con un número específico de ejecuciones
hyperfine --runs 20 'my_command'

# Benchmark con número mínimo de ejecuciones
hyperfine --min-runs 50 'my_command'

# Establecer tiempo mínimo de benchmark (segundos)
hyperfine --min-benchmarking-time 10 'my_command'

Comparación de comandos

# Comparar dos comandos
hyperfine 'find . -name "*.py"' 'fd -e py'

# Comparar múltiples comandos
hyperfine 'grep -r TODO .' 'rg TODO' 'ag TODO'

# Comparar con etiquetas legibles
hyperfine --command-name 'GNU grep' 'grep -r TODO .' \
          --command-name 'ripgrep' 'rg TODO' \
          --command-name 'silver searcher' 'ag TODO'

# Comparar implementaciones de ordenamiento
hyperfine 'sort large_file.txt' 'sort --parallel=4 large_file.txt'

# Comparar diferentes compiladores
hyperfine 'gcc -O2 -o test test.c && ./test' \
          'clang -O2 -o test test.c && ./test'

Ejecuciones de calentamiento

# Ejecutar iteraciones de calentamiento antes del benchmark (llena cachés)
hyperfine --warmup 5 'cat large_file.txt | wc -l'

# El calentamiento es esencial para:
# - Calentamiento de caché del sistema de archivos
# - Calentamiento de compilación JIT
# - Población de caché DNS
hyperfine --warmup 10 'curl -s http://localhost:8080/api/data > /dev/null'

# Comando de preparación (ejecutar antes de cada benchmark, no cronometrado)
hyperfine --prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches' \
          'cat large_file.txt | wc -l'

Configuración y limpieza

# Ejecutar un comando de configuración antes de la suite de benchmarks
hyperfine --setup 'make build' './my_program'

# Ejecutar un comando de preparación antes de CADA iteración (no cronometrado)
hyperfine --prepare 'cp original.txt test.txt' 'sort -o test.txt test.txt'

# Ejecutar un comando de limpieza después de cada iteración
hyperfine --cleanup 'rm -f output.txt' 'my_command > output.txt'

# Combinar configuración, preparación y limpieza
hyperfine \
  --setup 'gcc -O2 -o benchmark benchmark.c' \
  --prepare 'echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null' \
  --cleanup 'rm -f /tmp/bench_output' \
  './benchmark > /tmp/bench_output'

Listas de parámetros

# Benchmark con un rango de valores de parámetros
hyperfine --parameter-scan threads 1 8 './my_program --threads {threads}'

# Escaneo de parámetros con tamaño de paso
hyperfine --parameter-scan size 100 1000 --parameter-step-size 100 \
          './process --size {size}'

# Lista de parámetros (valores explícitos)
hyperfine --parameter-list lang 'python3,node,ruby' '{lang} script.{lang}'

# Múltiples listas de parámetros
hyperfine --parameter-list compiler 'gcc,clang' \
          --parameter-list opt '0,1,2,3' \
          '{compiler} -O{opt} -o test test.c && ./test'

# Benchmark de diferentes tamaños de archivo
hyperfine --parameter-list size 'small,medium,large' \
          --prepare 'cp data_{size}.csv input.csv' \
          './process input.csv'

Formatos de exportación

# Exportar como JSON (más detallado)
hyperfine --export-json results.json 'my_command'

# Exportar como CSV
hyperfine --export-csv results.csv 'my_command'

# Exportar como tabla Markdown
hyperfine --export-markdown results.md 'command1' 'command2'

# Exportar como AsciiDoc
hyperfine --export-asciidoc results.adoc 'command1' 'command2'

# Exportar en todos los formatos a la vez
hyperfine \
  --export-json results.json \
  --export-csv results.csv \
  --export-markdown results.md \
  'command1' 'command2'

# La salida JSON incluye:
# - Tiempos media, mediana, mínimo, máximo
# - Desviación estándar
# - Tiempos de ejecución individuales
# - Valores de parámetros (si se usaron)

Análisis estadístico

# Mostrar detección de valores atípicos estadísticos
hyperfine 'my_command'
# La salida muestra: media, min, max y marca valores atípicos

# Aumentar precisión con más ejecuciones
hyperfine --runs 100 'my_command'

# Ignorar las primeras N ejecuciones como valores atípicos
hyperfine --ignore-failure 'my_command'

# Mostrar tiempos de ejecución individuales
hyperfine --show-output 'my_command'

# Medir la sobrecarga de inicio del shell
hyperfine --shell=none 'my_command'

# Usar un shell específico
hyperfine --shell /bin/bash 'my_command'
hyperfine --shell /bin/zsh 'my_command'

# Sin shell (ejecución directa — evita sobrecarga del shell)
hyperfine -N './my_binary arg1 arg2'

Control de salida

# Mostrar salida del comando (no suprimir stdout/stderr)
hyperfine --show-output 'echo hello'

# Opciones de estilo
hyperfine --style full 'my_command'      # Barra de progreso completa (predeterminado)
hyperfine --style basic 'my_command'     # Salida de texto simple
hyperfine --style nocolor 'my_command'   # Sin colores ANSI
hyperfine --style color 'my_command'     # Salida coloreada
hyperfine --style none 'my_command'      # Salida mínima

# Ordenar resultados por tiempo medio
hyperfine --sort mean-time 'cmd1' 'cmd2' 'cmd3'

# Ordenar por nombre de comando
hyperfine --sort command 'cmd1' 'cmd2' 'cmd3'

Ejemplos del mundo real

# Comparar parsers JSON
hyperfine --warmup 3 \
  'python3 -c "import json; json.load(open(\"data.json\"))"' \
  'python3 -c "import orjson; orjson.loads(open(\"data.json\",\"rb\").read())"'

# Benchmark de consultas de base de datos
hyperfine --warmup 2 --runs 30 \
  'psql -c "SELECT count(*) FROM users" mydb' \
  'psql -c "SELECT count(*) FROM users WHERE active = true" mydb'

# Comparar compresión de archivos
hyperfine --warmup 1 --prepare 'cp original.tar test.tar' \
  'gzip test.tar' \
  --prepare 'cp original.tar test.tar' \
  'zstd test.tar' \
  --prepare 'cp original.tar test.tar' \
  'lz4 test.tar'

# Benchmark de caché de build Docker
hyperfine --warmup 1 --runs 5 \
  'docker build -t test .'

# Comparar motores de regex
hyperfine \
  "grep -cP '\d{3}-\d{3}-\d{4}' large_log.txt" \
  "rg -c '\d{3}-\d{3}-\d{4}' large_log.txt"

# Benchmark de endpoints HTTP
hyperfine --warmup 5 \
  'curl -s http://localhost:8080/api/v1/users > /dev/null' \
  'curl -s http://localhost:8080/api/v2/users > /dev/null'

# Probar rendimiento de E/S con caché frío
hyperfine \
  --prepare 'sync; echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null' \
  'cat /path/to/large/file > /dev/null'

Interpretación de resultados

# Ejemplo de salida:
# Benchmark 1: fd -e py
#   Time (mean +/- sigma):     12.3 ms +/-  1.2 ms    [User: 8.1 ms, System: 4.0 ms]
#   Range (min ... max):       10.5 ms ... 16.8 ms    50 runs
#
# - mean: tiempo promedio de ejecución
# - sigma: desviación estándar (menor = más consistente)
# - User: tiempo de CPU en modo usuario
# - System: tiempo de CPU en modo kernel
# - Range: ejecuciones más rápida y más lenta
# - "X runs" se realizaron para significancia estadística
#
# Summary:
#   fd -e py ran 12.45 +/- 3.21 times faster than find . -name "*.py"

Referencia rápida

OpciónPropósito
--runs NEstablecer número exacto de ejecuciones de benchmark
--warmup NEjecuciones de calentamiento antes de medir
--prepare CMDEjecutar antes de cada iteración de benchmark
--cleanup CMDEjecutar después de cada iteración de benchmark
--setup CMDEjecutar una vez antes de toda la suite
--parameter-scanIterar sobre un rango numérico
--parameter-listIterar sobre valores explícitos
--export-jsonExportar resultados JSON detallados
--export-csvExportar resultados CSV
--export-markdownExportar tabla Markdown
--command-nameEtiqueta para visualización
-N / --shell=noneOmitir shell, ejecutar binario directamente
--show-outputNo suprimir stdout/stderr
--ignore-failureContinuar con códigos de salida no-cero