Rendimiento y Paralelismo en Bash
Cuando los scripts deben procesar grandes cantidades de datos o archivos, hacerlo de forma secuencial puede ser muy lento. Bash ofrece varias formas de ejecutar tareas en paralelo y optimizar los pipelines.
1. Problemas comunes en scripts lentos #
- Procesar miles de archivos uno por uno en un bucle.
- Repetir tareas que podrían hacerse en paralelo.
- Usar pipelines ineficientes con muchas etapas innecesarias.
- Lanzar procesos externos dentro de bucles (
for,while) sin necesidad.
👉 Objetivo: minimizar procesos y aprovechar la paralelización.
2. Ejecutar tareas en segundo plano con & #
Se puede paralelizar fácilmente ejecutando comandos en background.
1#!/usr/bin/env bash 2 3echo "Procesando tareas..." 4sleep 3 & 5sleep 2 & 6sleep 1 & 7 8wait # espera a que terminen todos 9echo "Tareas finalizadas"
Salida (tiempo total ≈ 3 s en vez de 6 s):
Procesando tareas...
Tareas finalizadas
3. Paralelismo controlado con xargs -P #
xargs permite pasar una lista de elementos a un comando.
La opción -P indica cuántos procesos ejecutar en paralelo.
Ejemplo: convertir imágenes a PNG en paralelo:
1ls *.jpg | xargs -n1 -P4 convert -resize 800x800 {} {.}.png
-n1→ pasa 1 argumento cada vez-P4→ ejecuta 4 tareas en paralelo{}→ representa el archivo de entrada{.}→ nombre sin extensión
Ejemplo simple: dormir en paralelo #
1seq 1 5 | xargs -n1 -P3 bash -c 'echo "Procesando $1"; sleep $1' _
👉 Ejecuta 3 tareas en paralelo en lugar de una por una.
4. GNU parallel #
Herramienta más potente y flexible que xargs.
(A veces no viene instalada por defecto).
Instalación en Ubuntu/Debian:
1sudo apt install parallel
Ejemplo: redimensionar imágenes en paralelo:
1parallel convert {} {.}.png ::: *.jpg
:::→ separa el comando de la lista de argumentos.
Controlar número de trabajos #
1parallel -j 4 gzip {} ::: *.log
👉 Comprime archivos .log usando 4 procesos en paralelo.
Pipeline con parallel #
Ejemplo: procesar lista de URLs:
1cat urls.txt | parallel -j 5 "curl -s {} -o {#}.html"
-j 5→ 5 descargas simultáneas{#}→ índice del trabajo en ejecución
5. Sustitución de procesos <() y >() #
Permite usar la salida de un comando como si fuera un archivo temporal, sin escribir al disco.
1diff <(sort archivo1.txt) <(sort archivo2.txt)
👉 Compara dos archivos ordenados al vuelo, sin archivos intermedios.
6. Optimizar pipelines #
Cada | lanza un proceso adicional.
Minimiza etapas innecesarias.
Ejemplo ineficiente:
1cat archivo.txt | grep "ERROR" | sort | uniq -c
Ejemplo optimizado:
1grep "ERROR" archivo.txt | sort | uniq -c
👉 Evitamos el uso innecesario de cat.
7. Medir el tiempo de ejecución #
Puedes medir el tiempo total para evaluar mejoras:
1time ./mi_script.sh
O con el comando incorporado en scripts:
1START=$(date +%s) 2 # ... código ... 3END=$(date +%s) 4echo "Tiempo total: $((END-START)) segundos"
8. Ejemplo integrador: procesamiento masivo de logs #
Supón que tienes miles de archivos .log y quieres contar cuántas líneas con “ERROR” tiene cada uno y guardar el resultado en archivos separados.
1#!/usr/bin/env bash 2set -Eeuo pipefail 3 4mkdir -p resultados 5 6ls *.log | xargs -n1 -P4 bash -c ' 7 archivo="$1" 8 grep -c "ERROR" "$archivo" > "resultados/${archivo%.log}_errores.txt" 9' _
👉 Procesa los logs en paralelo con 4 procesos simultáneos.
9. Ejercicio práctico 1 #
Crea un script procesa_urls.sh que:
- Lea una lista de URLs de
urls.txt. - Descargue cada página usando
curl -s -o pagina_X.html. - Use
xargs -P4oparallelpara hacer varias descargas al mismo tiempo. - Muestre el tiempo total de ejecución.
10. Ejercicio práctico 2 #
Crea un script compara_archivos.sh que:
- Compare pares de archivos listados en
pares.txt(formato:archivo1 archivo2por línea). - Use
diffcon sustitución de procesos<()para comparar los archivos ordenados. - Haga las comparaciones en paralelo con
xargs -P2.
11. Ejercicio práctico 3 #
Crea un script comprime_logs.sh que:
- Encuentre todos los archivos
.logde más de 5 MB. - Los comprima en paralelo usando
parallel -j 4 gzip {}. - Muestre cuánto tiempo tardó todo el proceso.
12. Buenas prácticas #
- ✅ Paraleliza solo si las tareas son independientes entre sí.
- ✅ Ajusta el número de procesos (
-Po-j) según los núcleos de la CPU. - ✅ Usa sustitución de procesos para evitar archivos temporales.
- ✅ Optimiza los pipelines quitando comandos innecesarios.
- ✅ Mide el tiempo antes y después para comprobar mejoras reales.
Resumen del tema
Conceptos clave #
- Concurrencia Nativa con
&ywait: lanzamiento asíncrono de tareas independientes y sincronización colectiva de finalización. - Paralelismo Controlado con
xargs -P: ejecución concurrente limitando el número de procesos simultáneos (xargs -n1 -P4). - Orquestación Avanzada con GNU
parallel: distribución balanceada de cargas entre núcleos de CPU (parallel -j 4). - Sustitución de Procesos (
<(cmd)/>(cmd)): conexión directa de descriptores de archivo virtuales por FIFO/pipes sin escritura física en disco (diff <(sort a) <(sort b)). - Optimización de Pipelines: eliminación de intermediarios innecesarios (antipatrón useless use of cat
cat file | grep).
Qué debes recordar #
Acelera el procesamiento en lote usando xargs -P o GNU parallel y sustitución de procesos <(...) para evitar escrituras intermedias a disco.