unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Opportunistic GC
@ 2021-03-08  3:35 Stefan Monnier
  2021-03-08  7:20 ` Pip Cet
  0 siblings, 1 reply; 61+ messages in thread
From: Stefan Monnier @ 2021-03-08  3:35 UTC (permalink / raw)
  To: emacs-devel

I've been running with the code below recently to try and see if
opportunistic GC can be worth the trouble.

I've mostly focused on measuring the GC behavior rather than on tuning
the opportunistic parameters, so there's no doubt we can do better, but
here's what I've gotten so far:

- Gnus-focused session:
  ((opportunistic-gcs . 848)
   (jit 6 0 0) (cmd 526 241 707) (noncmd 92 1 2)
   (commands . 65745))
- General session:
  ((opportunistic-gcs . 1332)
   (jit 115 0 0) (cmd 239 19 89) (noncmd 1749 18 38)
   (commands . 176431))

The precise meaning of those numbers can be seen in the code, but here's
the explanation in words:
`commands` is the number of commands that were run.
`opportunistic-gcs` is the number of times we performed GC opportunistically
`jit`, `cmd`, and `noncmd` are the number of times we GC'd non-opportunistically
during a `cmd`, between commands (for `noncmd`) or during jit-lock.
The first number is the number of times a single GC took place
during/between the command, the second number counts the number of times
several GCs took place, and the third is to sum of those "multiple GCs".

The middle and third numbers are those for which opportunistic GC
fundamentally can't help very much: the command (or the work done
between commands) was just sufficiently long-running that it's hard to
avoid running a GC during this period.

[ Note that the way we measure for jit-lock it's really unlikely we'll
  ever get "multiple GCs" because it would require >1 GC performed
  during a single jit-lock chunk.  ]

Note that because my opportunistic GC calls (garbage-collect-maybe 3),
every opportunistic GC avoids only 1/3 non-opportunistic GC.

So, while these numbers don't look completely hopeless, they seem to
indicate that opportunistic GCs aren't very useful at least with the
tuning parameters I used.  To be more useful, maybe we'd need to
increase the non-opportunistic `gc-cons-percentage` and/or reduce the
idle-time before opportunistic GC (see "magic formula" below).

There's also a question of where those `noncmd` GCs come from.
(timers?  process filters?  non-jit part of redisplay? ...?)

        Stefan


(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.
  (when (garbage-collect-maybe 3)
    (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))





^ permalink raw reply	[flat|nested] 61+ messages in thread

end of thread, other threads:[~2022-07-05 13:49 UTC | newest]

Thread overview: 61+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-03-08  3:35 Opportunistic GC Stefan Monnier
2021-03-08  7:20 ` Pip Cet
2021-03-08  8:26   ` martin rudalics
2021-03-08  9:02     ` Pip Cet
2021-03-08  9:51       ` martin rudalics
2021-03-08 10:44         ` Pip Cet
2021-03-08 11:37           ` martin rudalics
2021-03-08 12:27             ` Pip Cet
2021-03-08 18:06               ` martin rudalics
2021-03-08 18:24                 ` Concurrent GC via fork (was: Opportunistic GC) Stefan Monnier
2021-03-09  8:10                   ` Concurrent GC via fork martin rudalics
2021-03-10 20:35                 ` Opportunistic GC Pip Cet
2021-03-08 11:38           ` Stefan Kangas
2021-03-08 12:55             ` Pip Cet
2021-03-08 14:06             ` Andrea Corallo via Emacs development discussions.
2021-03-08 14:49           ` Eli Zaretskii
2021-03-08 15:02             ` Pip Cet
2021-03-08 15:23               ` Eli Zaretskii
2021-03-08 15:39                 ` Pip Cet
2021-03-08 16:27                   ` Eli Zaretskii
2021-03-08 16:35                     ` Pip Cet
2021-03-08 17:06                       ` Eli Zaretskii
2021-03-08 15:46               ` Stefan Monnier
2021-03-08 16:11                 ` Pip Cet
2021-03-08 16:37                   ` Stefan Monnier
2021-03-08 14:01   ` Andrea Corallo via Emacs development discussions.
2021-03-08 15:04     ` Pip Cet
2021-03-08 15:50       ` Stefan Monnier
2021-03-08 15:52       ` Andrea Corallo via Emacs development discussions.
2021-03-08 16:14         ` Philipp Stephani
2021-03-08 16:18           ` Andrea Corallo via Emacs development discussions.
2021-03-08 16:40             ` Philipp Stephani
2021-03-08 16:44         ` Stefan Monnier
2021-03-08 17:13           ` Eli Zaretskii
2021-03-10 20:25             ` Pip Cet
2021-03-10 20:48               ` Eli Zaretskii
2021-03-11  7:55                 ` Pip Cet
2021-03-11  8:37                   ` Eli Zaretskii
2021-03-11  9:03                     ` Pip Cet
2021-03-11  9:34                       ` Eli Zaretskii
2021-03-11 10:11                         ` Pip Cet
2021-03-11 11:35                           ` Eli Zaretskii
2021-03-09  6:22           ` Richard Stallman
2021-03-09  8:11             ` Pip Cet
2021-03-10  5:52               ` Richard Stallman
2021-03-08 14:45   ` Eli Zaretskii
2021-03-08 14:57     ` Pip Cet
2021-03-08 15:16       ` Eli Zaretskii
2021-03-09 13:01       ` Eli Zaretskii
2021-03-10 20:21         ` Pip Cet
2021-03-10 20:41           ` Eli Zaretskii
2021-03-11  8:06             ` Pip Cet
2021-03-11  9:03               ` Eli Zaretskii
2021-03-08 16:03   ` Concurrent GC via `fork` (was: Opportunistic GC) Stefan Monnier
2021-03-08 16:25     ` Pip Cet
2021-03-08 17:37       ` Concurrent GC via `fork` Stefan Monnier
2021-03-08 19:21       ` Lars Ingebrigtsen
2021-03-10 17:18   ` Opportunistic GC Matt Armstrong
2021-03-10 19:38     ` Pip Cet
2022-07-05  1:47   ` Stefan Monnier
2022-07-05 13:49     ` T.V Raman

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).