Emacs memory consumption

The Emacs built-in command (garbage-collect) gives detailed information about the data structures that currently consume memory. It is propably not the most usefull information but I wanted to collect the data and plot it. I started with writing functions to access the list returned from (garbage-collect):

(defsubst get-mem-conses (mi)
  (let ((data (nth 0 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-symbols (mi)
  (let ((data (nth 1 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-misc (mi)
  (let ((data (nth 2 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-string-header (mi)
  (let ((data (nth 3 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-string-bytes (mi)
  (let ((data (nth 4 mi)))
    (/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))
 
(defsubst get-mem-vector-header (mi)
  (let ((data (nth 5 mi)))
    (/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))
 
(defsubst get-mem-vector-slots (mi)
  (let ((data (nth 6 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-floats (mi)
  (let ((data (nth 7 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-intervals (mi)
  (let ((data (nth 8 mi)))
    (/ (* (nth 1 data) (+ (nth 2 data) (nth 3 data))) (* 1024 1024.0))))
 
(defsubst get-mem-buffers (mi)
  (let ((data (nth 9 mi)))
    (/ (* (nth 1 data) (nth 2 data)) (* 1024 1024.0))))

Then I had need for a function that will be called periodically. This function will call (garbage-collect) and store the data in the file-system:

(defun collector (filename)
  "Write memory data into file with FILENAME."
  (let ((mi (garbage-collect)))
    (with-temp-buffer
      (insert 
       (format "%f %f %f %f %f %f %f %f %f %f %f\r\n"
               (float-time)
               (get-mem-conses mi)
               (get-mem-symbols mi)
               (get-mem-misc mi)
               (get-mem-string-header mi)
               (get-mem-string-bytes mi)
               (get-mem-vector-header mi)
               (get-mem-vector-slots mi)
               (get-mem-floats mi)
               (get-mem-intervals mi)
               (get-mem-buffers mi)))
      (let ((message-log-max nil))
        (append-to-file (point-min) (point-max) filename)))))

Next I have need for a function that starts the collection process and one that stops it again:

(defvar collector-timer nil)
 
(defun start-collection (filename interval)
  (interactive "FEnter filename:\nMEnter interval: ")
  (setq collector-filename filename
        collector-timer (run-at-time
                         2
                         (string-to-number interval)
                         'collector filename)))
(defun stop-collection ()
  (interactive)
  (when (timerp collector-timer)
    (cancel-timer collector-timer)))

Finally the collected data should be plotted into a nice graph:

(defun plot-data (datafile imagefile)
  (interactive "FEnter data-filename: \nFEnter image-filename:")
  (let ((gnuplot (start-process "gnuplot" "*gnuplot*" "gnuplot")))
    (process-send-string gnuplot "set term png\n")
    (process-send-string gnuplot (format "set output \"%s\"\n" imagefile))
    (process-send-string gnuplot "set grid\n")
    (process-send-string gnuplot "set title \"Emacs memory consumption by category\"\n")
    (process-send-string gnuplot "set xlabel \"interval\"\n")
    (process-send-string gnuplot "set autoscale\n")
    (process-send-string gnuplot "set ylabel \"2^{20} bytes\"\n")
    (process-send-string gnuplot (format "plot \"%s\" using 2 title \"cons cells\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 3 title \"symbols\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 4 title \"\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 5 title \"string header\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 6 title \"string bytes\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 7 title \"vector header\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 8 title \"vector slots\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 9 title \"floats\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 10 title \"intervals\" with lines" datafile))
    (process-send-string gnuplot (format ", \"%s\" using 11 title \"buffers\" with lines\n" datafile))))

Turns out that my emacs usage was really calm in the time when I sampled the data 🙂 In fact I have entered some kilobytes of test data into the scratch buffer with two seconds between two samples:

5 Kommentare

  1. I think he’s referring to the 10 defsubsts, which could be implemented as a single function–a macro isn’t even necessary. 🙂

    Thanks for sharing this! I’ll have to give it a try.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.