emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [ELPA] New package: emacs-gc-stats


From: Stefan Monnier
Subject: Re: [ELPA] New package: emacs-gc-stats
Date: Sat, 10 Jun 2023 12:33:43 -0400
User-agent: Gnus/5.13 (Gnus v5.13)

>> Let me know if you need help adding the package (I couldn't find a URL
>> for the code, so I have no opinion about it (yet :-))
> Oops... https://git.sr.ht/~yantar92/emacs-gc-stats

Thanks, added.

>> PS: In recent Emacsen, `gc-cons-percentage` is set to 1.0 when running in
>>     batch.  In non-batch I currently use 0.5 together with a timer that
>>     tries to opportunistically run the GC during idle time.
> This is yet another thing we can ask users to test, if we want to.
> My idea is have users set GC settings randomly in their init file,
> selecting from pre-configured set of alternatives we are yet to discuss.

FWIW, see below the code I'm using.  On my main Emacs instance,
`gc--opportunistic-score` tells me:

    ((jit 17 0 0)
     (cmd 22 3 7)
     (earlier-gcs . 76)
     (opportunistic-gcs . 1478)
     (noncmd 194 6 52)
     (commands . 109401))

IOW the majority of GCs happen "opportunistically" (i.e. during
idle time).  The `cmd/noncmd` distinction is supposed to distinguish
those GCs that happen while running timers and process filters, but I'm
not sure my code is careful enough to keep track of those correctly.


        Stefan


;;;; Opportunistic GC

(defvar gc--opportunistic-last-gcs gcs-done)
(defvar gc--opportunistic-state 'noncmd)
(defvar gc--opportunistic-counters nil)

(defun gc--check ()
  (let ((gcs-counted
         (+ (alist-get 'multi-gcs gc--opportunistic-counters 0)
                   (alist-get 'earlier-gcs gc--opportunistic-counters 0)
                   (alist-get 'single-gc-cmds gc--opportunistic-counters 0)
                   (alist-get 'noncmds-gcs gc--opportunistic-counters 0)
                   (alist-get 'opportunistic-gcs gc--opportunistic-counters 0)
                   (or (car (alist-get 'error-gcs gc--opportunistic-counters)) 
0))))
    (unless (= gcs-done gcs-counted)
      (push (+ (- gcs-done gcs-counted)
               (or (car (alist-get 'error-gcs gc--opportunistic-counters)) 0))
            (alist-get 'error-gcs gc--opportunistic-counters)))))

(defun gc--opportunistic-record (nextstate)
  (let ((counts (alist-get gc--opportunistic-state gc--opportunistic-counters)))
    (unless counts
      (setf (alist-get gc--opportunistic-state gc--opportunistic-counters)
            (setq counts (list 0 0 0))))
    (pcase (prog1 (- gcs-done gc--opportunistic-last-gcs)
             (setq gc--opportunistic-last-gcs gcs-done))
      ((pred (>= 0)) nil)
      (1 (cl-incf (nth 0 counts)))
      (gcs (cl-incf (nth 1 counts))
           (cl-incf (nth 2 counts) gcs))))
  (setq gc--opportunistic-state nextstate))

(defun gc--opportunistic-postch ()
  (cl-incf (alist-get 'commands gc--opportunistic-counters 0))
  (gc--opportunistic-record 'noncmd))

(defun gc--opportunistic-prech ()
  (cl-callf identity
      (alist-get 'earlier-gcs gc--opportunistic-counters gcs-done))
  (gc--opportunistic-record 'cmd)
  ;; (gc--check)
  )

(defun gc--opportunistic-jitlock (orig-fun start)
  (if (eq gc--opportunistic-state 'cmd)
      ;; Count jit-lock execution which happens during a command as
      ;; being part of command execution rather than as part of jit-lock!
      (funcall orig-fun start)
    (let ((gc--opportunistic-state gc--opportunistic-state))
      (gc--opportunistic-record 'jit)
      (unwind-protect
          (funcall orig-fun start)
        (gc--opportunistic-record 'postjit)))))

(add-hook 'pre-command-hook #'gc--opportunistic-prech)
(add-hook 'post-command-hook #'gc--opportunistic-postch)
(advice-add 'jit-lock-function :around #'gc--opportunistic-jitlock)

(defun gc--opportunistic ()
  "Run the GC during idle time."
  ;; This is good for two reasons:
  ;; - It reduces the number of times we have to GC in the middle of
  ;;   an operation.
  ;; - It means we GC when the C stack is short, reducing the risk of false
  ;;   positives from the conservative stack scanning.
  (unless (> gc-cons-percentage 0.1)
    (setq gc-cons-percentage 0.5))
  (when (garbage-collect-maybe 10)
    (cl-incf (alist-get 'opportunistic-gcs gc--opportunistic-counters 0))
    ;; Don't double count this GC in other categories.
    (cl-incf gc--opportunistic-last-gcs)
    ;; Recalibrate the timer.
    (cancel-function-timers #'gc--opportunistic)
    (run-with-idle-timer
     ;; FIXME: Magic formula!
     (+ 1 (* 10 (/ gc-elapsed gcs-done))) t #'gc--opportunistic)))


(defun gc--opportunistic-score ()
  "Show the current counters's that keep track of GC behavior."
  (interactive)
  (message "%S" gc--opportunistic-counters))




reply via email to

[Prev in Thread] Current Thread [Next in Thread]