performancerustengineering

Ingeniería de Rendimiento: Optimización Zero-Copy IPC

Cómo redujimos las asignaciones de heap en un 80% en la ruta crítica de OmniMon usando IPC zero-copy, caché con Arc y ordenamiento por referencia.

El Costo de Clonar

El puente IPC de OmniMon transfiere métricas del sistema desde el backend Rust al frontend Svelte cada 2 segundos. Con más de 500 procesos activos, cada transferencia clonaba cientos de structs — creando presión innecesaria en el heap y pausas de GC.

Nos propusimos eliminar cada asignación redundante.

Optimización 1: Cachear hw.memsize al Inicio

Antes: OmniMon generaba un subproceso sysctl -n hw.memsize cada 2 segundos para leer la memoria total del sistema.

Después: Un OnceLock<Option<u64>> cachea el valor en la primera llamada. La memoria total del sistema no cambia en tiempo de ejecución.

Impacto: Elimina 0.5 spawns de subprocesos por segundo.

Optimización 2: Consumir Caché del Watcher

Antes: Funciones como top_processes_by_memory() creaban instancias frescas de System::new_all() — activando escaneos completos del sistema.

Después: Todas las funciones de métricas leen primero del caché existente SystemState del watcher, con fallback para casos extremos.

Impacto: ~60% de reducción en overhead de syscalls de CPU en la ruta crítica.

Optimización 3: Ordenamiento por Referencia

Antes: La función get_metrics() clonaba el vector completo de más de 500 procesos, lo ordenaba y luego lo truncaba a los 100 principales.

Después: Crea un Vec<&CachedProcessInfo> de referencias, ordena in-place y luego clona solo las 100 entradas principales.

Impacto: ~80% menos asignaciones de heap por llamada IPC (~400 clones de struct eliminados).

Optimización 4: Caché de Pestañas con Arc

Antes: Cada lectura del caché de pestañas del navegador clonaba todo el Vec<BrowserTab> más todos los campos String — una operación O(n) con ~60 asignaciones de strings.

Después: El caché almacena Arc<Vec<BrowserTab>>. Las lecturas clonan solo el puntero Arc — un único incremento atómico, O(1).

Impacto: Elimina ~60 asignaciones de String por llamada a get_browser_tabs().

Optimización 5: Seguridad ante Panics

Antes: Una macro unreachable!() al final de send_with_retry() podía crashear toda la app si el ciclo de reintentos salía inesperadamente.

Después: Reemplazado con un Err("Unexpected exit from retry loop") explícito — propagación de error elegante en lugar de un panic en producción.

Optimizaciones del Frontend

La capa Svelte recibió optimizaciones equivalentes:

  • Virtual scrolling refinado para mantener 60 FPS con más de 2000 procesos
  • Búsqueda con debounce (150ms) previene filtrado O(n) por cada tecla
  • Reactividad basada en stores evita re-renders innecesarios de componentes

Resultados

MétricaAntesDespués
Asignaciones de heap por llamada IPC~500~100
Spawns de subprocesos / segundo0.50
Lecturas de caché de pestañasO(n)O(1)
Clones en ordenamiento de procesos500+100

Estas optimizaciones se acumulan: OmniMon ahora maneja más de 2000 procesos simultáneos con latencia de UI imperceptible.