Come misuro le prestazioni del codice elisp?


26

Come misuro le prestazioni del mio codice elisp? Quali strumenti / pacchetti esterni sono disponibili per misurare il tempo impiegato?

Oltre al tempo totale, posso vedere un profilo che mostra il tempo impiegato per funzione? Posso profilare anche l'uso della memoria?


1
La domanda è troppo ampia. Che tipo di esibizione? Dove? Quando? " Emacs performance " può significare qualsiasi cosa.
Disegnò il

@Drew Molti altri linguaggi di programmazione hanno una serie di parametri di riferimento (ad esempio Python: speed.pypy.org , JS: Sunspider ecc.) E speravo che ci fosse un equivalente per l'interprete elisp.
Wilfred Hughes,

Il benchmarking come quello fornito dalla funzione benchmarke dal profiler non misura le prestazioni di Emacs . Misura le prestazioni valutando espressioni particolari. È utile nel confrontare le performance all'interno di Emacs. Per misurare le prestazioni di Emacs stesso dovresti confrontarlo con le prestazioni di qualcosa di diverso da Emacs. Ed è qui che entra in gioco l'ampiezza di Emacs. Potresti misurare Emacs vs XYZ per questo o quello, ma per misurare le prestazioni di Emacs nel loro insieme avresti bisogno di innumerevoli simili confronti.
Disegnò il

Forse intendevi " Come misuro le prestazioni in Emacs "?
Disegnò il

2
OK, ho aperto emacs.stackexchange.com/q/655/304 per fare il benchmarking di Emacs e ho riformulato questa domanda per fare il benchmarking / profiling dei programmi elisp.
Wilfred Hughes,

Risposte:


31

segno di riferimento

Le opzioni più semplici sono il benchmarkpacchetto integrato . Il suo utilizzo è straordinariamente semplice:

(benchmark 100 (form (to be evaluated)))

È caricato automaticamente, quindi non è nemmeno necessario richiederlo.

profiling

Il benchmark è buono nei test generali, ma se riscontri problemi di prestazioni non ti dice quali funzioni stanno causando il problema. Per questo, hai il profiler (anche incorporato) .

  1. Inizia con M-x profiler-start.
  2. Eseguire alcune operazioni che richiedono tempo.
  3. Ottieni il rapporto con M-x profiler-report.

Dovresti essere indirizzato a un buffer con un albero navigabile di chiamate di funzione.
Schermata del profiler


benchmarkla funzione non sembra funzionare: quando faccio all'interno di un .cfile aperto (benchmark 100 (c-font-lock-fontify-region 0 17355)), continuo a ricevere void-function jit-lock-bounds.
Ciao Angelo

1
FTR: in alternativa a benchmarkci sono funzioni benchmark-rune benchmark-run-compiled. Per me la differenza principale era che entrambe le funzioni funzionano effettivamente (vedi il commento precedente) : Ь
Hi-Angel

14

Oltre alla risposta di @ Malabara, tendo a utilizzare una with-timermacro su misura per strumentare in modo permanente varie parti del mio codice (ad esempio il mio init.elfile).

La differenza è che, sebbene benchmarkpermetta di studiare le prestazioni di un determinato bit di codice che tu strumenti, with-timerti dà sempre il tempo speso in ogni parte strumentata del codice (senza sovraccarico per parti sufficientemente grandi), che ti dà l'input da conoscere quale parte dovrebbe essere esaminata ulteriormente.

(defmacro with-timer (title &rest forms)
  "Run the given FORMS, counting the elapsed time.
A message including the given TITLE and the corresponding elapsed
time is displayed."
  (declare (indent 1))
  (let ((nowvar (make-symbol "now"))
        (body   `(progn ,@forms)))
    `(let ((,nowvar (current-time)))
       (message "%s..." ,title)
       (prog1 ,body
         (let ((elapsed
                (float-time (time-subtract (current-time) ,nowvar))))
           (message "%s... done (%.3fs)" ,title elapsed))))))

Esempio di utilizzo:

(with-timer "Doing things"
  (form (to (be evaluated))))

producendo il seguente output nel *Messages*buffer:

Doing things... done (0.047s)

Devo dire che questo è fortemente ispirato alla use-package-with-elapsed-timermacro di Jon Wiegley nella sua eccellente use-packageestensione.


Se stai misurando init.el, probabilmente sarai interessato al profiler di avvio di emacs .
Wilfred Hughes,

Le macro sono fantastiche. Questo merita più voti.
Malabarba,

2
Emacs registra il tempo di inizializzazione totale. Puoi mostrarlo con il comando emacs-init-time.
Joe,

1
@WilfredHughes sì, lo uso esupe mi piace. Ma ancora una volta, l'interesse per una cosa come quella with-timernon è tanto quello di profilare qualcosa in modo così profondo. Il vero interesse è che hai sempre informazioni di profilazione. Ogni volta che avvio emacs, ho una serie di linee nel mio *Messages*buffer che mi dicono quale parte ha impiegato quanto tempo. Se rilevo qualcosa di anormale, posso quindi utilizzare uno degli strumenti più adeguati per profilare e ottimizzare le cose.
ffevotte,

@JoeS Sì, emacs-init-timeproduce informazioni interessanti. Tuttavia, fornisce solo un tempo trascorso inclusivo, senza la possibilità di scomporre singole parti dell'inizializzazione.
ffevotte,

3

Oltre alla risposta di @ Malabarba, tieni presente che puoi misurare il tempo di esecuzione compilato del tuo codice con benchmark-run-compiled. Quella metrica è spesso molto più pertinente del tempo di esecuzione interpretato che M-x benchmarkti dà:

ELISP> (benchmark-run (cl-loop for i below (* 1000 1000) sum i))
(0.79330082 6 0.2081620540000002)

ELISP> (benchmark-run-compiled (cl-loop for i below (* 1000 1000) sum i))
(0.047896284 0 0.0)

I tre numeri sono il tempo totale trascorso, il numero di esecuzioni GC e il tempo trascorso in GC.


1

Il benchmarking non si basa solo sull'ottenimento dei numeri, ma anche sul prendere decisioni basate sull'analisi dei risultati.

C'è un pacchetto benchstat.el su MELPA che puoi usare per ottenere le funzionalità fornite dal programma benchstat .

Implementa benchmark basati sul confronto in cui si esaminano Xle proprietà delle prestazioni Y.

Le funzioni di Benchstat possono essere visualizzate come un benchmark-run-compiledwrapper che non solo raccoglie le informazioni, ma le restituisce in un formato di interpretazione di facile lettura. Include:

  • Delta del tempo trascorso tra XeY
  • Tempo medio medio
  • Importo delle allocazioni

Esempio di utilizzo molto semplice:

(require 'benchstat)

;; Decide how much repetitions is needed.
;; This is the same as `benchmark-run-compiled` REPETITIONS argument.
(defconst repetitions 1000000)

;; Collect old code profile.
(benchstat-run :old repetitions (list 1 2))
;; Collect new code profile.
(benchstat-run :new repetitions (cons 1 2))

;; Display the results.
;; Can be run interactively by `M-x benchstat-compare'.
(benchstat-compare)

Il benchstat-comparerendering renderà i risultati in un buffer temporaneo:

name   old time/op    new time/op    delta
Emacs    44.2ms ± 6%    25.0ms ±15%  -43.38%  (p=0.000 n=10+10)

name   old allocs/op  new allocs/op  delta
Emacs      23.0 ± 0%      11.4 ± 5%  -50.43%  (p=0.000 n=10+10)

benchstatTuttavia, avrai bisogno del programma binario. Se hai usato il linguaggio di programmazione Go, molto probabilmente ne hai già uno nel tuo sistema. Altrimenti esiste un'opzione per compilarlo dai sorgenti.

Il binario precompilato per linux / amd64 è disponibile nella pagina di rilascio di github .

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.