Come posso migliorare i tempi di avvio nonostante molti pacchetti?


19

TL; DR Ho una quantità così grande di pacchetti che sta danneggiando il mio tempo di avvio. Se non credete che potrebbe essere il caso, continuate a leggere.


Il tempo di avvio di My Emacs è piuttosto ridotto. Non lo uso use-package, ho appena impostato tonnellate di hook e autoloads in modo che quasi tutto il codice venga rinviato. In realtà il tutto viene caricato di solito in meno di mezzo secondo, nonostante sembri un casino folle.

Tuttavia, nel corso del tempo ho notato che il mio tempo di avvio viene minuziosamente più lento, inspiegabilmente. Questo alla fine è arrivato al punto in cui il tempo di avvio è ≥ 1 secondo. Alla fine ne ho avuto abbastanza e ho scavato nella radice del problema. Alla fine ho commentato tutto il mio ~/.emacsfile e ho scoperto che il tempo di avvio era ancora ≥ 1 secondo. In effetti, si era rasato solo ~ 0.2secondi, a volte anche meno. Quindi ho provato emacs -qe ho scoperto che il tempo di avvio era ~ 0.1secondi.

Dopo aver esaminato questa sezione del manuale Elisp, ho scoperto perché emacs -qstava riducendo così tanto i tempi di avvio. Apparentemente emacs -qimpedisce a Emacs di fare tre cose all'avvio:

  1. caricamento del file init
  2. caricamento del default.elfile
  3. chiamata package-initialize

Abbiamo già escluso il mio file init, dal momento che commentare il mio intero ~/.emacsnon fa quasi nulla. Non uso un default.elfile, quindi anche questo è escluso. Il che lascia package-initializeil colpevole del colpo di scena.

Perché package-initializeimpiegare così tanto tempo di avvio? Questa è stata la prima domanda che mi sono posto. Non sto caricando tutto automaticamente? Beh si. Ma questo è esattamente il problema.

Ho trovato questo post che spiega che "l'attivazione" dei pacchetti consiste nella lettura dei file di caricamento automatico e nell'impostazione dei percorsi di caricamento. Ciò comporta ovviamente una penalità I / O quando si hanno molti pacchetti perché si hanno molti file di caricamento automatico da leggere e molti percorsi da impostare. Sfortunatamente, senza questo, il compito di gestire i caricamenti automatici cade nelle mani dell'utente. In altre parole, senza lasciare package.elscorrere il filesystem per i file e i percorsi di caricamento automatico, dovrei gestirlo da solo, il che potrebbe essere un processo noioso e soggetto a errori.

Preferirei non percorrere quella strada. Al momento ho 116 pacchetti, di cui 107 di quelli ELPA e 25 dei quali sono dipendenze. Sono sicuro che questo numero enorme è ciò che sta degradando così tanto la mia performance. Ma sono in un dilemma perché non voglio rimuovere nessuno dei miei pacchetti.

Esiste un rimedio in una situazione del genere per riavere il mio tempo di avvio del lampo?


Aggiornare:

Abbiamo iniziato un nuovo thread nella emacs-develmailing list su alcune patch di Stefan Monnier (una descrizione di queste patch è qui ) per risolvere questo problema. Chiunque è invitato a testare le proprie patch e fornire feedback.

Un altro aggiornamento:

Sembra che Stefan Monnier o non sia più interessato a questo problema o non stia ricevendo i miei messaggi. Sono propenso a credere al primo, il che va bene, anche se apprezzerei un qualche tipo di risposta da parte sua se fosse così. Ad ogni modo, il codice che ha prodotto per questo problema finora funziona abbastanza bene. Le sue patch più recenti sono disponibili qui (per Emacs 25.3) e qui (per il ramo master di Emacs).Ho visto buoni miglioramenti nel mio tempo di avvio grazie alle sue patch e sono a un punto in cui mi sento a mio agio con il mio tempo di avvio in quanto è ottimizzato il più possibile senza tagliare le funzionalità della mia personalizzazione. Speravo che queste patch sarebbero entrate nella linea principale di Emacs ad un certo punto, ma immagino che io (o qualcun altro) avrei dovuto prendere la torcia per ora, invece di Stefan. Avevamo un po 'di parsimonia nella mailing list sull'assegnazione del copyright e le licenze. Inizialmente ero a disagio nel farlo, ma a causa di alcuni commenti di Richard Stallman e altri, la cessione del copyright potrebbe non essere così restrittiva come pensavo inizialmente. Inoltre, potrebbe essere possibile per me affidare le mie opere di dominio pubblico in alternativa all'assegnazione del copyright.

In ogni caso, grazie Stefan per le patch finora! Spero che continuerai a sviluppare questi cambiamenti, ma in caso contrario, va bene e potrei continuare a svilupparli ad un certo punto. Ringrazio anche tutti gli altri che hanno offerto spunti e contributi per risolvere questo problema.

Ancora un altro aggiornamento:

Wow, sembra che questa funzione sia finalmente arrivata e sarà in Emacs 27. Grazie a Stefan Monnier!


Ottima domanda
Estratto il

use-packageè la strada da percorrere per questo.
Dodgie,

Non tentare di minimizzare il problema (la latenza di avvio è importante!), Ma considera di eseguire emacs come demone / server in modo da pagare una sola volta il costo di avvio.
GManNickG,

1
@GManNickG Eseguo Emacs come server. Sfortunatamente, ogni tanto spingo troppo Emacs o lo armeggio troppo e un riavvio è il modo migliore per ripulire le cose. Quando ciò si verifica, mi piace che il tempo di avvio sia ottimale.
PIL2

Risposte:


13

Una delle scelte progettuali in package.el è stata quella di provare a rendere le cose "semplici". Parte di questo è che package-initializecerca tutti i pacchetti installati, quindi cerca di capire quali di questi dovrebbero essere attivati ​​(in base al pinning e alla recency delle versioni nel caso in cui siano disponibili più versioni dello stesso pacchetto), quindi carica ognuno attiva il <pkg>-autoloads.elfile del pacchetto .

Quindi per N pacchetti installati, ciò significa sostanzialmente leggere N <pkg>-pkg.elfile di descrizione del pacchetto e N <pkg>-autoloads.elfile. Per le grandi N, questo può diventare un problema serio. Un altro potenziale problema di prestazioni è che aggiungerà N elementi a load-path, quindi ogni volta che loadEmacs cercherà tra le directory N, quindi ognuno loadverrà rallentato.

Ci sono vari modi in cui possiamo provare ad accelerare questo:

  • Fornisci un modo per precompilare un ~/.emacs.d/elpa/package-initialize.el(c)file che sarebbe il risultato della concatenazione di tutto <pkg>-autoloads.elil giusto nell'ordine giusto. Quindi package-initializepotrebbe semplicemente caricare questo file quando presente e saltare tutto il resto. Avresti quindi bisogno di un modo per aggiornare / scaricare il package-initialize.el(c)file quando i pacchetti vengono aggiunti / aggiornati / rimossi o quando cambi il tuo package-pinned-packageso il tuo package-load-list. Penso che questo possa essere fatto con abbastanza poche modifiche al sistema (l'unica cosa che avrebbe davvero bisogno di essere modificata penso sia package-initializeche possa essere detto di "solo attivare" senza caricare i metadati sui pacchetti disponibili).

  • Fornisci un modo per compilare / manipolare i superpacchetti, ovvero pacchetti che combinano più pacchetti in uno (quindi c'è solo un elemento aggiunto load-path, uno <pkg>-pkg.ele uno <pkg>-autoloads.elcaricato). Questo potrebbe essere più difficile da fare (perché in questo caso non è possibile attivare solo una parte dei pacchetti contenuti in tali superpacchetti, quindi l'analisi di dipendenza / versione potrebbe essere complicata).

La prima opzione sopra dovrebbe essere abbastanza facile da implementare e renderebbe package-initialize molto più veloce quando hai installato molti pacchetti. Se sei interessato a provarlo, non esitare a chiedermi aiuto.

FWIW, ho appena provato a creare un tale file di mega-caricamento automatico "a mano" sul mio setup di test. Risultati: mentre package-initializeimpiega circa 0,9 secondi, il caricamento del mega-autoloads.elfile richiede 0,3 secondi, che posso portare a 0,2 secondi mediante l'associazione load-source-file-functiona zero e a 0,1 secondi tramite la compilazione di byte del file. Mi aspettavo una maggiore velocità, a dire il vero, ma vale comunque la pena.

[EDIT] Questo approccio "mega autoloads" è ora disponibile nel ramo principale di Emacs (per diventare Emacs-27 in un futuro lontano). È controllato dalla nuova package-quickstartvariabile.


Queste sono alcune idee molto interessanti. Il primo sembra più realistico e meno problematico. Il secondo è piuttosto interessante, ma sembra un lavoro per gli package.elsviluppatori. Che tipo di consiglio hai per iniziare con quella prima opzione? Mi piacerebbe vedere cosa riesco a risolvere, poiché sembra molto più praticabile.
PIL2,

el-get utilizza l'approccio a file a caricamento automatico singolo, praticamente funziona quasi sempre. Ci sono problemi con alcuni pacchetti i cui caricamenti automatici dipendono dalla posizione nel filesystem in cui vengono valutati. Non seguo quello che intendi per "nel giusto ordine", perché l'ordine del caricamento automatico importa sempre (no pensi che fosse anche deterministico per l'attuale package.el)?
npostavs,

@Stefan, per favore, condividi qui la soluzione, se possibile.
Manuel Uberti,

1
@npostavs: la maggior parte dei <pkg>-autoloads.elfile installa solo i caricamenti automatici e in effetti non importa dell'ordinamento, ma non c'è nulla che impedisca loro di fare altre cose casuali e package.el garantisce che il pacchetto da cui <pkg>dipende verrà attivato prima di <pkg>sé.
Stefan,

1
Un altro aggiornamento su questo tema: abbiamo iniziato un nuovo argomento nella mailing list qui e chiunque è libero di commentare o di prova i cambiamenti di Stefan.
PIL2

6

Il problema che descrivi riguardo package-initializeal tempo impiegato per caricare è un problema ben noto. È anche uno dei problemi che alcuni framework emacs tentano di risolvere caricando manualmente i caricamenti automatici.

Vedo due soluzioni al tuo problema.

  1. Scrivi (o estrai da un framework) la funzionalità per impostare i percorsi e caricare i caricamenti automatici dei pacchetti che ti interessano.
  2. Utilizzare un framework che miri esplicitamente alla velocità. Personalmente raccomando emacs DOOM . Con questo framework, sto caricando più di 200 pacchetti in circa 1 secondo.

Uno dei principali reson per la raccomandazione di emacs DOOM è che il framework pone la gestione dei pacchetti al di fuori di emacs. Non fraintendermi, è ancora Emacs che sta facendo la gestione dei pacchetti, è solo che la gestione dei pacchetti è fatta al di fuori di una sessione utente standard. La filosofia qui è: quando normalmente si avvia emacs, dovremmo essere in grado di presumere che tutti i pacchetti siano presenti e possano già essere caricati. Questo fa risparmiare molto tempo. DOOM emacs fornisce una sorta di equivalente di apt-geto pacmanper emacs. Una volta installato un pacchetto, ogni volta che si avvia emacs si presume che sia già installato; Nessuna domanda chiesta.


Accidenti, felice di sapere che questo problema non mi riguarda solo. Grazie per avermi indicato DOOM Emacs. Dovrò dare un'occhiata più da vicino e vedere cosa posso adattare alla mia configurazione.
PIL2,

Gioco da qualche tempo con emacs DOOM (e contribuisco quando posso). In caso di problemi, non esitate a contattarmi.
UndeadKernel,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.