Bagaimana cara mengukur kinerja kode elisp?


26

Bagaimana cara mengukur kinerja kode elisp saya? Alat / paket eksternal apa yang tersedia bagi saya untuk mengukur waktu yang dibutuhkan?

Selain total waktu, dapatkah saya melihat profil yang menunjukkan waktu yang diambil per fungsi? Bisakah saya profil penggunaan memori juga?


1
Pertanyaannya terlalu luas. Performa seperti apa? Dimana? Kapan? " Kinerja Emacs " dapat berarti apa saja.
Drew

@Rew Banyak bahasa pemrograman lain memiliki seperangkat tolok ukur (misalnya Python: speed.pypy.org , JS: Sunspider dll), dan saya berharap ada padanan yang setara untuk interpreter elisp.
Wilfred Hughes

Benchmarking seperti yang disediakan oleh fungsi benchmarkdan profiler tidak mengukur kinerja Emacs . Ini mengukur kinerja mengevaluasi ekspresi tertentu. Sangat membantu dalam membandingkan kinerja dalam Emacs. Untuk mengukur kinerja Emacs sendiri, Anda perlu membandingkannya dengan kinerja selain Emacs. Dan di situlah keluasan Emacs ikut bermain. Anda dapat mengukur Emacs vs XYZ untuk ini atau itu, tetapi untuk mengukur kinerja Emacs secara keseluruhan, Anda perlu beberapa perbandingan seperti itu.
Drew

Mungkin maksud Anda " Bagaimana cara mengukur kinerja di Emacs "?
Drew

2
OK, saya sudah membuka emacs.stackexchange.com/q/655/304 untuk menjadi tentang benchmarking Emacs, dan menulis ulang pertanyaan ini tentang benchmarking / profiling program elisp.
Wilfred Hughes

Jawaban:


31

Tolok ukur

Opsi yang paling mudah adalah benchmarkpaket bawaan. Penggunaannya sangat sederhana:

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

Diisi otomatis, jadi Anda bahkan tidak perlu memerlukannya.

Pembuatan profil

Benchmark bagus dalam pengujian keseluruhan, tetapi jika Anda mengalami masalah kinerja, itu tidak memberi tahu Anda fungsi mana yang menyebabkan masalah. Untuk itu, Anda memiliki profiler (juga built-in) .

  1. Mulai dengan M-x profiler-start.
  2. Lakukan beberapa operasi memakan waktu.
  3. Dapatkan laporannya M-x profiler-report.

Anda harus dibawa ke buffer dengan pohon panggilan fungsi yang dapat dinavigasi.
Tangkapan layar profiler


benchmarkfungsi sepertinya tidak berfungsi: ketika saya melakukannya di dalam .cfile yang dibuka (benchmark 100 (c-font-lock-fontify-region 0 17355)), saya terus mendapatkan void-function jit-lock-bounds.
Hi-Angel

1
FTR: sebagai alternatif benchmarkada fungsi benchmark-rundan benchmark-run-compiled. Bagi saya perbedaan utamanya adalah kedua fungsi tersebut benar-benar berfungsi (lihat komentar sebelumnya) : Ь
Hi-Angel

14

Selain jawaban @ Malabara, saya cenderung menggunakan with-timermakro yang dibuat khusus untuk secara permanen instrumen berbagai bagian kode saya (misalnya init.elfile saya ).

Perbedaannya adalah bahwa sementara benchmarkmemungkinkan untuk mempelajari kinerja sedikit kode spesifik yang Anda instrumenkan, with-timerselalu memberi Anda waktu yang dihabiskan di setiap bagian kode yang diinstrumentasi (tanpa banyak biaya overhead untuk bagian yang cukup besar), yang memberi Anda masukan untuk mengetahui bagian mana yang harus diselidiki lebih lanjut.

(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))))))

Contoh penggunaan:

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

menghasilkan output berikut dalam *Messages*buffer:

Doing things... done (0.047s)

Saya harus menyebutkan bahwa ini sangat terinspirasi oleh use-package-with-elapsed-timermakro Jon Wiegley dalam use-packageekstensi yang luar biasa .


Jika Anda mengukur init.el, Anda mungkin akan tertarik dengan profiler startup emacs .
Wilfred Hughes

Macro luar biasa. Ini layak mendapat lebih banyak suara.
Malabarba

2
Emacs mencatat total waktu init. Anda bisa menunjukkannya dengan perintah emacs-init-time.
Joe

1
@ WillfredHughes ya, saya menggunakan esupdan saya menyukainya. Tetapi sekali lagi, minat hal seperti with-timeritu tidak begitu banyak untuk profil sesuatu yang sama sekali. Minat sebenarnya adalah bahwa Anda selalu memiliki informasi profil. Setiap kali saya memulai emacs, saya memiliki baris di *Messages*buffer saya yang memberitahu saya bagian mana yang memakan waktu berapa lama. Jika saya mendeteksi sesuatu yang tidak normal, saya dapat menggunakan alat yang lebih memadai untuk membuat profil dan mengoptimalkan berbagai hal.
ffevotte

@ JoS Ya, emacs-init-timememang menghasilkan informasi yang menarik. Namun, itu hanya memberikan waktu berlalu yang inklusif, tanpa kemungkinan untuk memecah bagian individu dari inisialisasi.
ffevotte

3

Selain jawaban @ Malabarba, perhatikan bahwa Anda dapat mengukur waktu eksekusi yang dikompilasi dengan kode Anda benchmark-run-compiled. Metrik itu seringkali jauh lebih relevan daripada waktu eksekusi yang ditafsirkan yang M-x benchmarkmemberi Anda:

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)

Tiga angka tersebut adalah total waktu yang telah berlalu, jumlah GC yang berjalan, dan waktu yang dihabiskan dalam GC.


1

Benchmarking bukan hanya tentang mendapatkan angka, tetapi juga tentang membuat keputusan berdasarkan analisis hasil.

Ada paket benchstat.el pada MELPA yang dapat Anda gunakan untuk mendapatkan fitur yang disediakan oleh program benchstat .

Ini mengimplementasikan pembandingan berbasis perbandingan tempat Anda menguji Xproperti kinerja Y.

Fungsi Benchstat dapat dilihat sebagai benchmark-run-compiledpembungkus yang tidak hanya mengumpulkan informasi, tetapi juga mengembalikannya dalam format interpretasi yang mudah dibaca. Itu termasuk:

  • Delta waktu yang telah berlalu antara XdanY
  • Berarti waktu rata-rata
  • Jumlah alokasi

Contoh penggunaan yang sangat sederhana:

(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)

Surat benchstat-comparewasiat akan menghasilkan buffer sementara:

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)

Anda akan membutuhkan benchstatprogram biner. Jika Anda menggunakan bahasa pemrograman Go, kemungkinan besar Anda sudah memilikinya di sistem. Kalau tidak, ada opsi untuk mengkompilasinya dari sumber.

Biner terkompilasi untuk linux / amd64 dapat ditemukan di halaman rilis github .

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.