¿Cómo puedo mejorar el tiempo de inicio a pesar de muchos paquetes?

19

TL; DR Tengo una cantidad tan grande de paquetes que está perjudicando mi tiempo de inicio. Si no crees que ese podría ser el caso, sigue leyendo.


Mi tiempo de inicio de Emacs es bastante pequeño. No lo uso use-package, solo configuro toneladas de ganchos y autoloads para que se difiera casi todo el código. En realidad, todo se carga en menos de medio segundo, a pesar de que parece un desastre loco.

Sin embargo, con el tiempo me di cuenta de que mi tiempo de inicio se pone minuciosamente más lenta, inexplicablemente. Esto finalmente ha llegado al punto donde el tiempo de inicio es ≥ 1 segundo. Finalmente tuve suficiente y profundicé en la raíz del problema. Finalmente comenté todo mi ~/.emacsarchivo y descubrí que el tiempo de inicio todavía era ≥ 1 segundo. De hecho, solo se había reducido ~ 0.2segundos, a veces incluso menos. Luego intenté emacs -qy descubrí que el tiempo de inicio era de ~ 0.1segundos.

Al examinar esta sección del manual de Elisp, descubrí por qué emacs -qestaba reduciendo tanto el tiempo de inicio. Aparentemente emacs -qimpide que Emacs haga tres cosas al inicio:

  1. cargando su archivo de inicio
  2. cargando su default.elarchivo
  3. vocación package-initialize

Ya hemos descartado mi archivo de inicio, ya que comentar todo ~/.emacsno hace casi nada. No uso un default.elarchivo, por lo que también se descarta. Lo que deja package-initializecomo el culpable del éxito de rendimiento.

¿Por qué estaría package-initializetomando tanto tiempo de inicio? Esa fue la primera pregunta que me hice. ¿No estoy cargando todo automáticamente? Bueno, sí. Pero ese es precisamente el problema.

Encontré esta publicación que explica que "activar" paquetes consiste en leer archivos de carga automática y establecer rutas de carga. Obviamente, esto incurre en una penalización de E / S cuando tiene muchos paquetes porque tiene muchos archivos de carga automática para leer y muchas rutas para configurar. Desafortunadamente, sin esto, la tarea de administrar las cargas automáticas cae en manos del usuario. En otras palabras, sin dejar que package.elrastree el sistema de archivos para la carga automática de archivos y rutas, tendría que administrarlo yo mismo, lo que podría ser un proceso tedioso y propenso a errores.

Preferiría no ir por ese camino. Actualmente tengo 116 paquetes, de los cuales 107 son de ELPA y 25 de los cuales son dependencias. Estoy seguro de que este enorme número es lo que está degradando tanto mi rendimiento. Pero estoy en un dilema porque no quiero eliminar ninguno de mis paquetes.

¿Existe algún remedio en tal situación para recuperar el tiempo de inicio de mi rayo?


Actualizar:

Hemos comenzado un nuevo hilo en la emacs-devellista de correo sobre algunos parches de Stefan Monnier (una descripción de estos parches está aquí ) para resolver este problema. Cualquiera puede probar sus parches y dar su opinión.

Otra actualización:

Parece que Stefan Monnier ya no está interesado en este problema o no está recibiendo mis mensajes. Me inclino a creer lo primero, lo cual está bien, aunque agradecería algún tipo de respuesta de él si ese fuera el caso. De todos modos, el código que ha producido para este problema hasta ahora funciona bastante bien. Los parches más recientes suyos se pueden encontrar aquí (para Emacs 25.3) y aquí (para la rama maestra de Emacs).He visto buenas mejoras en mi tiempo de inicio gracias a sus parches y estoy en un punto en el que me siento cómodo con mi tiempo de inicio de tal manera que esté lo más optimizado posible sin eliminar las características de mi personalización. Esperaba que estos parches llegaran a la línea principal de Emacs en algún momento, pero supongo que yo (o alguien más) tendría que tomar la antorcha ahora, en lugar de Stefan. Tuvimos un poco de discusión en la lista de correo sobre asignación de derechos de autor y licencias. Inicialmente me sentí incómodo al hacerlo, pero debido a algunos comentarios de Richard Stallman y otros, la asignación de derechos de autor puede no ser tan restrictiva como pensaba originalmente. Además, es posible que pueda comprometer mis trabajos al dominio público como una alternativa a la asignación de derechos de autor.

En cualquier caso, ¡gracias Stefan por los parches hasta ahora! Espero que continúes desarrollando estos cambios, pero si no, está bien y puedo continuar desarrollándolo en algún momento. También agradezco a todos los demás que ofrecieron información y contribución para resolver este problema.

Otra actualización más:

Wow, parece que esta característica finalmente aterrizó y estará en Emacs 27. ¡Gracias a Stefan Monnier!

PIB2
fuente
Gran pregunta
Dibujó el
use-packagees el camino a seguir para esto.
Dodgie
No intento minimizar el problema (¡la latencia de inicio es importante!), Pero considere ejecutar emacs como un demonio / servidor para que solo pague el costo de inicio una vez.
GManNickG
1
@GManNickG Ejecuto Emacs como servidor. Desafortunadamente, de vez en cuando presiono demasiado a Emacs o jugueteo demasiado y un reinicio es la mejor manera de limpiar las cosas. Cuando eso ocurre, me gusta que mi tiempo de inicio sea óptimo.
GDP2

Respuestas:

13

Una de las opciones de diseño en package.el era intentar hacer las cosas "simples". Parte de esto es que package-initializebusca todos los paquetes que están instalados, luego trata de averiguar cuáles de ellos deben activarse (de acuerdo con la fijación y la antigüedad de las versiones en caso de que estén disponibles varias versiones del mismo paquete), luego carga cada uno activa el <pkg>-autoloads.elarchivo del paquete .

Entonces, para N paquetes instalados, eso significa básicamente leer N <pkg>-pkg.elarchivos de descripción de paquete y N <pkg>-autoloads.elarchivos. Para N grandes, eso puede convertirse en un problema grave. Otro posible problema de rendimiento es que agregará N elementos load-path, por lo que cada vez que loadEmacs busque a través de N directorios, cada uno loadse ralentizará.

Hay varias formas en que podemos intentar acelerar esto:

  • Proporcione alguna forma de calcular previamente un ~/.emacs.d/elpa/package-initialize.el(c)archivo que sería el resultado de concatenar todo <pkg>-autoloads.elen el orden correcto. Entonces package-initializepodría cargar este archivo cuando esté presente y omitir todo lo demás. Luego, necesitaría alguna forma de actualizar / vaciar el package-initialize.el(c)archivo cuando se agregan / actualizan / eliminan paquetes o cuando cambia su package-pinned-packageso su package-load-list. Creo que esto se puede hacer con muy pocos cambios en el sistema (creo que lo único que realmente necesitaría cambiar es package-initializepara que se le pueda decir que "solo se active" sin cargar los metadatos sobre los paquetes disponibles).

  • Proporcione alguna forma de construir / manipular superpaquetes, es decir, paquetes que combinan varios paquetes en uno (por lo que solo se agrega un elemento load-path, uno <pkg>-pkg.ely uno <pkg>-autoloads.elcargado). Esto podría ser más difícil de hacer (porque entonces no puede activar solo una parte de los paquetes contenidos en dichos superpaquetes, por lo que el análisis de dependencia / versión podría ser complicado).

La primera opción anterior debería ser bastante fácil de implementar y sería package-initialize mucho más rápida cuando tenga muchos paquetes instalados. Si está interesado en probar esto, no dude en pedirme ayuda.

FWIW, acabo de intentar construir un archivo de megacargas automáticas "a mano" en mi configuración de prueba. Resultados: mientras package-initializetoma alrededor de 0.9s, la carga del mega-autoloads.elarchivo toma 0.3s que puedo reducir a 0.2s al vincular load-source-file-functiona cero, y a 0.1s al compilar el archivo en bytes. Esperaba una mejor velocidad, para ser honesto, pero aún así vale la pena.

[EDITAR] Este enfoque de "megacargas automáticas" ahora está disponible en la rama maestra de Emacs (para convertirse en Emacs-27 en un futuro lejano). Está controlado por la nueva package-quickstartvariable.

Stefan
fuente
Esas son algunas ideas muy interesantes. El primero suena más realista y menos desafiante. El segundo es bastante interesante, pero eso suena como un trabajo para los package.eldesarrolladores. ¿Qué tipo de consejo tienes para comenzar con esa primera opción? Me gustaría ver qué puedo hacer con él, ya que parece mucho más viable.
GDP2
el-get utiliza el enfoque de archivo de carga automática única, básicamente funciona la mayor parte del tiempo. Hay problemas con algunos paquetes cuyas cargas automáticas dependen de la ubicación en el sistema de archivos en el que se evalúan. Sin embargo, no entiendo lo que quiere decir con "en el orden correcto", ¿por qué el orden de carga automática siempre importa? cree que incluso fue determinista para el paquete actual.
npostavs
@Stefan, comparte la solución aquí, si es posible.
Manuel Uberti
1
@npostavs: la mayoría de los <pkg>-autoloads.elarchivos solo configuran las cargas automáticas y, de hecho, no les importa ordenar, pero no hay nada que les impida hacer otras cosas al azar, y package.el garantiza que el paquete del que <pkg>depende se activará antes que él <pkg>mismo.
Stefan
1
Otra actualización sobre este tema: hemos comenzado un nuevo tema en la lista de correo aquí y cualquiera puede comentar o probar los cambios de Stefan.
GDP2
6

El problema que describe sobre package-initializetomar tanto tiempo para cargar es un problema bien conocido. También es uno de los problemas que algunos frameworks de emacs intentan resolver cargando las cargas automáticas manualmente.

Veo dos soluciones a tu problema.

  1. Escriba (o extraiga de un marco) la funcionalidad para establecer las rutas y cargar las cargas automáticas de los paquetes que le interesan.
  2. Use un marco que explícitamente apunte a la velocidad. Yo personalmente recomiendo DOOM emacs . Con este marco, estoy cargando más de 200 paquetes en aproximadamente 1 segundo.

Una de las principales razones para recomendar DOOM emacs es que el marco coloca la administración de paquetes fuera de emacs. No me malinterpreten, todavía es emacs el que gestiona los paquetes, es solo que la gestión de paquetes se realiza fuera de una sesión de usuario estándar. La filosofía aquí es: cuando normalmente se inicia emacs, deberíamos poder suponer que todos los paquetes están presentes y que ya se pueden cargar. Esto ahorra mucho tiempo. DOOM emacs proporciona una especie del equivalente de apt-geto pacmanpara emacs. Una vez que se instala un paquete, cada vez que se inicia emacs se supone que ya está instalado; no se hicieron preguntas.

UndeadKernel
fuente
Uf, me alegra saber que este problema no solo me está afectando. Gracias por señalarme a DOOM Emacs. Tendré que echarle un vistazo más de cerca y ver qué puedo adaptar a mi propia configuración.
GDP2
He estado jugando con DOOM emacs (y contribuyendo cuando puedo) desde hace un tiempo. Si tiene problemas, no dude en ponerse en contacto conmigo.
UndeadKernel