wandres.dev
RENDIMIENTO · un editor instantáneo

Medir el arranque: leer el informe de --startuptime

Generar un informe de arranque y leerlo columna por columna, distinguir tiempo acumulado de tiempo propio, establecer una linea base con --clean para separar el coste del nucleo del de tu configuracion, y medir con rigor estadistico: repeticiones, mediana, cache frio frente a caliente y el instante en que el editor deja de ser util.

⏱ 20 min

Casi todo lo que se dice sobre el arranque de un editor es folclore: cifras recordadas de memoria, comparaciones entre máquinas distintas, plugins acusados sin pruebas. El problema no es que la gente no mida, es que mide una vez, en caliente, sin línea base y sin entender qué representa cada número. Neovim trae de fábrica un instrumento honesto que registra, con resolución de microsegundos, cada fichero que carga y cada fase que atraviesa desde que el proceso nace hasta que la primera pantalla está pintada. Este capítulo trata de leerlo bien, porque el resto del nivel —el caché de bytecode, el perfilador, la carga perezosa— solo tiene sentido si antes sabes atribuir milisegundos a culpables concretos.

🎯 Al terminar esta lección sabrás
  • Generar un informe con --startuptime y leer sus tres columnas sin confundirlas.
  • Situar las fases del arranque y saber cuál marca el final del tiempo que el usuario percibe.
  • Establecer una línea base con --clean y aislar el coste atribuible a tu configuración.
  • Medir con rigor: repeticiones, mediana, caché frío frente a caliente, y el efecto de abrir un fichero.

Generar el informe

La opción escribe el registro al terminar el proceso, de modo que la forma útil de invocarla es pidiendo la salida y la salida inmediata en la misma orden. Si abres el editor y te quedas dentro, el fichero no aparece hasta que sales, y habrás medido también tu tiempo de reacción.

# Arranque en vacio, sin abrir ningun fichero
nvim --startuptime /tmp/inicio.log +q

# Arranque abriendo un fichero real: mide lo que de verdad haces
nvim --startuptime /tmp/inicio-rust.log +q src/main.rs

# Linea base del nucleo: sin configuracion, sin plugins, sin shada
nvim --clean --startuptime /tmp/base.log +q

La diferencia entre las dos primeras órdenes es la primera gran lección del nivel: el arranque en vacío es una ficción de laboratorio. Lo que tú haces al trabajar es abrir un fichero, y ese fichero dispara detección de tipo, resaltado, plegado y arranque del servidor de lenguaje, todo ello antes de entrar en el bucle principal. Medir siempre en vacío es la manera más cómoda de obtener una cifra bonita que no describe tu experiencia.

times in msec
 clock   self+sourced   self:  sourced script
 clock   elapsed:              other lines

000.005  000.005: --- NVIM STARTING ---
000.312  000.307: locale set
000.418  000.106: inits 1
001.002  000.584: inits 2
001.640  000.638: init highlight
014.882  013.070  013.070: sourcing /home/u/.config/nvim/init.lua
015.001  000.119: sourcing vimrc file
018.774  001.902  000.410: sourcing /usr/share/nvim/runtime/filetype.lua
041.220  000.140: opening buffers
041.884  000.664: BufEnter autocommands
042.933  000.923: VimEnter autocommands
043.512  000.579: UIEnter autocommands
044.001  000.489: before starting main loop
051.774  007.773: first screen update
051.802  000.028: --- NVIM STARTED ---

Leer las tres columnas

El propio informe declara su formato en la cabecera, y ahí está el malentendido más frecuente. Las líneas que corresponden a un fichero cargado tienen tres números; las demás, dos. La primera columna es siempre el reloj: milisegundos acumulados desde el nacimiento del proceso, y por tanto crece monótonamente. Nunca sumes esa columna: sumar tiempos acumulados no significa nada.

En las líneas de fichero, la segunda columna es el coste propio más el de todo lo que ese fichero cargó a su vez, y la tercera es el coste propio, con las cargas anidadas descontadas. La diferencia entre ambas es exactamente lo que ese fichero delegó en otros. Por eso el fichero de configuración principal suele mostrar un segundo número enorme y un tercero modesto: no es lento, es que arrastra a todo el gestor de plugins detrás. La columna que sirve para acusar a alguien es la tercera; la que sirve para entender la estructura del arranque es la segunda. En las líneas de dos números, el segundo es simplemente el tiempo transcurrido desde la línea anterior, es decir, el coste de esa fase.

⚠️
Ordenar por la columna equivocada

Los guiones que circulan por ahí ordenan el informe por la segunda columna y presentan un ranking de culpables. Ese ranking está sesgado: premia a los ficheros que están alto en el árbol de dependencias, que son precisamente los que menos trabajo hacen por sí mismos. Ordena por la tercera columna, self, cuando busques quién consume; usa la segunda para decidir qué rama del árbol podar entera.

Hay una consecuencia práctica de esa aritmética que conviene enunciar, porque explica por qué tanta gente concluye que su arranque es un misterio. La suma de todas las terceras columnas más la de las fases sin fichero debería aproximarse al total, y cuando no lo hace es porque una parte sustancial del tiempo no está en cargar nada, sino en ejecutar trabajo enganchado a las fases finales: autocomandos, cálculo del primer redibujado, arranque de procesos externos. Ese hueco entre la suma y el total es, de hecho, la magnitud más interesante del informe, porque señala la parte de tu arranque que ningún caché de módulos podrá tocar jamás.

Las fases sin fichero asociado son igual de informativas. La detección de tipo de fichero, la apertura de buffers y los autocomandos de BufEnter, VimEnter y UIEnter aparecen como entradas propias, y ahí es donde se esconde el coste que ningún plugin admite tener: trabajo enganchado a eventos en lugar de a la carga de un módulo.

flowchart TB
a[Proceso creado] --> b[Inicializacion del nucleo]
b --> c[Lectura de la configuracion]
c --> d[Gestor de plugins y plugins de arranque]
d --> e[Apertura de buffers y deteccion de tipo]
e --> f[VimEnter y UIEnter]
f --> g[before starting main loop]
g --> h[first screen update]
style g fill:#cba6f7,color:#11111b
style h fill:#a6e3a1,color:#11111b

La marca before starting main loop es la frontera que casi todo el mundo cita como si fuera el resultado, y no lo es. Es el instante en que el editor empieza a aceptar teclas, pero la pantalla todavía no está pintada. La marca que corresponde a lo que un ser humano llama arranque es first screen update, y entre una y otra puede haber una cantidad sorprendente de milisegundos si tu línea de estado, tu tema o tu columna de signos calculan mucho en el primer redibujado.

La línea base: qué es núcleo y qué es tuyo

Un número aislado es inútil porque no sabes cuánto de él es inevitable. El editor sin configuración también tarda: inicializa su tabla de opciones, carga su tiempo de ejecución mínimo, negocia con el terminal. Esa cantidad es tu suelo, y depende del disco, del sistema de ficheros y de la máquina, no de tus decisiones.

# Suelo del nucleo, sin ninguna configuracion
nvim --clean --startuptime /tmp/base.log +q
tail -3 /tmp/base.log

# Tu configuracion completa
nvim --startuptime /tmp/mio.log +q
tail -3 /tmp/mio.log

La resta de ambas cifras, tomada sobre la marca del primer redibujado, es el coste atribuible a ti, y es la única magnitud que puedes reducir. Entre las dos hay peldaños intermedios que conviene conocer para localizar de qué capa viene el gasto: la opción que impide leer cualquier fichero de configuración, la que evita cargar los plugins del tiempo de ejecución, y la que sustituye tu configuración por otra distinta, con la que puedes bisecar tu propio arranque cargando la mitad de tus módulos.

nvim -u NONE --startuptime /tmp/a.log +q          # sin configuracion, con plugins de runtime
nvim --noplugin --startuptime /tmp/b.log +q        # tu config, sin plugins del runtime
nvim -u /tmp/mitad.lua --startuptime /tmp/c.log +q # biseccion manual de tu propia config

La bisección merece una defensa explícita, porque parece artesanal frente a un perfilador y en la práctica es más rápida. Un informe te dice que un fichero costó doce milisegundos; no te dice si esos doce milisegundos desaparecerían al quitarlo o se trasladarían a otro sitio, porque las dependencias se comparten. Cortar la configuración por la mitad y medir responde a la pregunta que de verdad importa, que no es cuánto cuesta algo, sino cuánto ahorrarías quitándolo.

Medir con rigor

Una sola ejecución no es una medida. La variación entre arranques consecutivos en la misma máquina supera con facilidad el diez por ciento por causas ajenas al editor: planificación del sistema, escalado de frecuencia del procesador, estado del caché de páginas del sistema operativo. La primera ejecución tras un reinicio lee de disco lo que las siguientes leen de memoria, y esa diferencia entre frío y caliente puede ser de un factor cinco. Ambas cifras son legítimas y describen cosas distintas: el arranque frío es el que sufres la primera vez del día; el caliente, el de las siguientes cien.

# Diez repeticiones, quedandonos con la marca final de cada una
for i in $(seq 10); do
  nvim --startuptime /tmp/r.log +q
  tail -1 /tmp/r.log | awk '{print $1}'
done | sort -n | awk '{v[NR]=$1} END {print "mediana", v[int(NR/2)+1]}'

# O con una herramienta que ya sabe de estadistica
hyperfine --warmup 3 'nvim --headless +q'

Usa la mediana, no la media: la distribución tiene cola derecha y un solo arranque desafortunado desplaza la media lo suficiente para hacerte perseguir un fantasma. Y compara siempre en la misma sesión de terminal, con la misma máquina despierta y sin nada compilando de fondo, porque comparar una medida de hoy con otra de la semana pasada es comparar dos ordenadores diferentes que casualmente tienen el mismo nombre.

Quedan dos advertencias sobre el propio banco de pruebas. La primera es que el modo sin interfaz, cómodo para automatizar, no pinta nada, de manera que suprime justo la fase final que más te interesa y además evita la negociación de capacidades con el terminal. Es una herramienta excelente para comparar dos configuraciones entre sí y una mala referencia absoluta. La segunda es que el emulador de terminal forma parte del sistema medido: la latencia de arranque de una interfaz gráfica, la de una sesión remota y la de un multiplexor difieren lo suficiente para invalidar cualquier comparación entre cifras obtenidas en entornos distintos, y esa es la razón por la que las cifras que la gente publica en foros no significan nada para ti.

📝
Dos números que no debes confundir

El informe termina con una marca de arranque completado que incluye el primer redibujado, pero el editor ya aceptaba teclas antes. Si tu objetivo es que escribir sea posible cuanto antes, optimiza hacia la marca del bucle principal. Si tu objetivo es que la pantalla esté completa y correcta cuanto antes, optimiza hacia la del primer redibujado. Son metas compatibles pero no idénticas, y perseguir la segunda a veces empeora la primera.

El informe no mide el editor: mide tus decisiones acumuladas

Hay un cambio de perspectiva que separa a quien ajusta un arranque de quien lo entiende, y consiste en dejar de leer el informe como una lista de culpables y empezar a leerlo como la autobiografía de tu configuración. Cada línea con un tiempo propio apreciable es una decisión que tomaste en algún momento y que sigue cobrándose su precio en cada arranque, indefinidamente, hayas usado esa funcionalidad hoy o no. Ese encuadre reordena las prioridades de una manera que la simple aritmética no consigue. Un plugin que cuesta veinte milisegundos y usas cada día es una compra razonable; uno que cuesta tres y usas dos veces al mes es peor negocio, aunque el ranking lo sitúe abajo, porque su coste es fijo y su beneficio es esporádico. La consecuencia práctica es que el objetivo no es minimizar la cifra final, que es una métrica de vanidad fácil de falsear moviendo trabajo detrás del primer redibujado, sino maximizar la proporción del arranque que corresponde a cosas que realmente usas. Existe además un límite duro por debajo del cual seguir optimizando deja de tener sentido, y conviene conocerlo antes de invertir una tarde: por debajo del umbral en que la percepción humana deja de distinguir un instante de otro, alrededor de los cien milisegundos, todo el trabajo adicional se convierte en un pasatiempo. La medida existe para permitirte gastar el presupuesto con criterio, no para llevarte a un mínimo ascético en el que el editor arranca al instante y no hace nada de lo que necesitas. La pregunta correcta ante cada línea del informe nunca es cuánto cuesta, sino qué compra.

⚔️ Tu informe de referencia
  1. Genera tres informes con la misma orden: en vacío, abriendo un fichero de tu lenguaje principal, y con la opción de configuración limpia. Anota en los tres la marca del primer redibujado.
  2. Ordena tu informe completo por la tercera columna y quédate con los cinco ficheros de mayor coste propio. Comprueba si coinciden con los cinco que obtendrías ordenando por la segunda, y explica cada discrepancia.
  3. Mide la diferencia entre arranque frío y caliente forzando el vaciado del caché de páginas de tu sistema, y decide cuál de las dos cifras vas a usar como referencia a partir de ahora.
  4. Ejecuta diez repeticiones, calcula mediana y recorrido intercuartílico, y determina a partir de qué magnitud una mejora sería distinguible del ruido en tu máquina.
  5. Bisecciona tu configuración: crea una versión que cargue solo la mitad de tus módulos, mide, y repite hasta encontrar el módulo que aporta el mayor salto. Compáralo con lo que decía el ranking.