Subject: bug#77775: 30.1; Proced performance

From: Rahguzar <[email-masked]>
Date: Sun, 13 Apr 2025 12:08:04 +0500

Dear Emacs developers,

I sometimes see pauses when I have a proced buffer with auto update enabled. As a benchmark I tried
M-: (benchmark-run-compiled 100 (proced-update t t)) RET
(8.874007655 23 0.4591267360000001)

>From profiler the what takes most time is:

1) Call to `proced-process-attributes' in `proced-update'. This is unavoidable.
2) Another call to `proced-process-attributes' in `proced-format'. This call gets attributes for all the process although it only need them for one.

I tried the diff
diff --git a/lisp/proced.el b/lisp/proced.el
index 51e6f3aca4d..c1a27cd0b15 100644
--- a/lisp/proced.el
+++ b/lisp/proced.el
@@ -1748,7 +1748,7 @@ proced-format
   ;; If none of the alternatives is non-nil, the attribute is ignored
   ;; in the listing.
   (let ((standard-attributes
-         (car (proced-process-attributes (list-system-processes))))
+         (car (proced-process-attributes (last (list-system-processes)))))
         new-format fmi)
     (if (and proced-tree-flag
              (assq 'ppid standard-attributes))

(The comment at the start of this diff is outdated)

After this:
M-: (benchmark-run-compiled 100 (proced-update t t)) RET
(5.910094172 18 0.4095661129999999)

3) `proced-format-pid' takes a lot of time. The culprit here is a call to `(process-attributes pid)'. To avoid it, I added a new variable `proced-format-current-process` which is let bound to the process being formatted in `proced-format'. This can then be used by format functions to look up other attributes if needed. The diff this time is larger:

diff --git a/lisp/proced.el b/lisp/proced.el
index c1a27cd0b15..42782615b33 100644
--- a/lisp/proced.el
+++ b/lisp/proced.el
@@ -428,6 +428,9 @@ proced-sort-internal
   "Sort scheme for listing (internal format).
 It is a list of lists (KEY PREDICATE REVERSE).")
 
+(defvar proced-format-current-process nil
+  "Symbol holding the process that is being formatted by function `proced-format'.")
+
 (defvar proced-marker-char ?*		; the answer is 42
   "In Proced, the current mark character.")
 
@@ -1682,13 +1685,13 @@ proced-format-state
 
 (defun proced-format-pid (pid)
   "Format PID."
-  (let ((proc-info (process-attributes pid))
-        (pid-s (number-to-string pid)))
+  (let ((pid-s (number-to-string pid)))
     (cond ((and proced-enable-color-flag
                 (not (file-remote-p default-directory))
                 (equal pid (emacs-pid)))
            (propertize pid-s 'font-lock-face 'proced-emacs-pid))
-          ((and proced-enable-color-flag (equal pid (alist-get 'sess proc-info)))
+          ((and proced-enable-color-flag
+                (equal pid (alist-get 'sess proced-format-current-process)))
            (propertize pid-s 'font-lock-face 'proced-session-leader-pid))
           (proced-enable-color-flag
            (propertize pid-s 'font-lock-face 'proced-pid))
@@ -1814,7 +1817,8 @@ proced-format
                  (end-of-line)
                  (setq value (cdr (assq key (cdr process))))
                  (insert (if value
-                             (apply #'propertize (funcall fun value) fprops)
+                             (let ((proced-format-current-process process))
+                               (apply #'propertize (funcall fun value) fprops))
                            (format (concat "%" (number-to-string (nth 3 grammar)) "s")
                                    unknown))
                          whitespace)
@@ -1828,7 +1832,9 @@ proced-format
                (dolist (process process-alist)
                  (end-of-line)
                  (setq value (cdr (assq key (cdr process))))
-                 (insert (if value (apply #'propertize (funcall fun value) fprops)
+                 (insert (if value
+                             (let ((proced-format-current-process process))
+                               (apply #'propertize (funcall fun value) fprops))
                            unknown))
                  (forward-line))
                (push (apply #'propertize (nth 1 grammar) hprops) header-list))
@@ -1839,7 +1845,8 @@ proced-format
                  (dolist (process process-alist)
                    (setq value (cdr (assq key (cdr process))))
                    (if value
-                       (setq value (apply #'propertize (funcall fun value) fprops)
+                       (setq value (let ((proced-format-current-process process))
+                                     (apply #'propertize (funcall fun value) fprops))
                              width (max width (length value))
                              field-list (cons value field-list))
                      (push unknown field-list)

but I think straightforward. Now:
M-: (benchmark-run-compiled 100 (proced-update t t)) RET
(4.046857128 16 0.441410039)

Together these two cut down the time `proced-update' by more than a half and the pauses are now much less noticeable.

Please let me know if these changes are acceptable and I will prepare a patch.

Thanks,
Rahguzar

In GNU Emacs 30.1 (build 1, aarch64-redhat-linux-gnu, GTK+ Version
 3.24.43, cairo version 1.18.2) of 2025-03-01 built on
 aebb4e0b2cb247d5ab04316e77c1c8ce
System Description: Fedora Linux Asahi Remix 41 (Forty One)

Configured using:
 'configure --build=aarch64-redhat-linux --host=aarch64-redhat-linux
 --program-prefix= --disable-dependency-tracking --prefix=/usr
 --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin
 --sysconfdir=/etc --datadir=/usr/share --includedir=/usr/include
 --libdir=/usr/lib64 --libexecdir=/usr/libexec --localstatedir=/var
 --runstatedir=/run --sharedstatedir=/var/lib --mandir=/usr/share/man
 --infodir=/usr/share/info --disable-gc-mark-trace --with-cairo
 --with-dbus --with-gif --with-gpm=no --with-harfbuzz --with-jpeg
 --with-modules --with-native-compilation=aot --with-pgtk --with-png
 --with-rsvg --with-sqlite3 --with-tiff --with-tree-sitter --with-webp
 --with-xpm build_alias=aarch64-redhat-linux
 host_alias=aarch64-redhat-linux CC=gcc 'CFLAGS=-DMAIL_USE_LOCKF -O2
 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches
 -pipe -Wall -Werror=format-security
 -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS
 -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong
 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
 -mbranch-protection=standard -fasynchronous-unwind-tables
 -fstack-clash-protection -fno-omit-frame-pointer
 -mno-omit-leaf-frame-pointer ' 'LDFLAGS=-Wl,-z,relro -Wl,--as-needed
 -Wl,-z,pack-relative-relocs -Wl,-z,now
 -specs=/usr/lib/rpm/redhat/redhat-hardened-ld
 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1
 -specs=/usr/lib/rpm/redhat/redhat-package-notes ' CXX=g++ 'CXXFLAGS=-O2
 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches
 -pipe -Wall -Werror=format-security
 -Wp,-U_FORTIFY_SOURCE,-D_FORTIFY_SOURCE=3 -Wp,-D_GLIBCXX_ASSERTIONS
 -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong
 -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
 -mbranch-protection=standard -fasynchronous-unwind-tables
 -fstack-clash-protection -fno-omit-frame-pointer
 -mno-omit-leaf-frame-pointer '
 PKG_CONFIG_PATH=:/usr/lib64/pkgconfig:/usr/share/pkgconfig'

Configured features:
ACL CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GSETTINGS HARFBUZZ JPEG
LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 MODULES NATIVE_COMP NOTIFY INOTIFY
PDUMPER PGTK PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP XIM GTK3 ZLIB

Important settings:
  value of $LANG: en_US.utf8
  locale-coding-system: utf-8-unix

Major mode: mu4e:main

Minor modes in effect:
  windmove-mode: t
  corfu-history-mode: t
  corfu-popupinfo-mode: t
  server-mode: t
  mu4e-search-minor-mode: t
  mu4e-update-minor-mode: t
  mu4e-context-minor-mode: t
  dirvish-override-dired-mode: t
  satchel-mode: t
  nerd-icons-completion-mode: t
  marginalia-mode: t
  vertico-multiform-mode: t
  vertico-mode: t
  evil-goggles-mode: t
  anzu-mode: t
  evil-mode: t
  evil-local-mode: t
  recentf-mode: t
  savehist-mode: t
  save-place-mode: t
  electric-pair-mode: t
  override-global-mode: t
  pixel-scroll-precision-mode: t
  repeat-mode: t
  midnight-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  size-indication-mode: t
  line-number-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  abbrev-mode: t

Load-path shadows:
/home/azeem/.local/state/emacs/elpaca/builds/transient/transient hides /usr/share/emacs/30.1/lisp/transient
/home/azeem/.local/state/emacs/elpaca/builds/ef-themes/theme-loaddefs hides /usr/share/emacs/30.1/lisp/theme-loaddefs

Features:
(shadow emacsbug proced windmove oc-basic ol-eww eww-hacks tex-mode
shell eww url-queue mm-url ol-rmail ol-mhe ol-irc ol-info ol-gnus
nnselect ol-docview ol-bibtex ol-bbdb ol-w3m ol-doi org-link-doi view
helpful cc-langs cc-vars cc-defs trace cl-print edebug debug backtrace
info-look info dash smerge-mode diff dabbrev delsel face-remap
nerd-icons-corfu consult-imenu vc-git diff-mode track-changes
vc-dispatcher fabs consult vertico-directory pdf-roll pdf-annot tablist
tablist-filter semantic/wisent/comp semantic/wisent
semantic/wisent/wisent semantic/util-modes semantic/util semantic
semantic/tag semantic/lex semantic/fw mode-local cedet pdf-outline
inspirehep-pdf companion-mode inspirehep reftex reftex-loaddefs
reftex-vars bibtex pdf-links pdf-isearch let-alist pdf-misc imenu
pdf-tools package pdf-view pdf-cache pdf-info tq pdf-util pdf-macs
help-fns radix-tree vertico-sort doc-view jka-compr undo-fu-session
dired-x dirvish-emerge dired-aux diredfl gnus-dired dirvish-subtree
dirvish-icons dirvish-widgets url-handlers jit-spell ispell cape
mastodon-media mastodon-profile mastodon-auth mastodon-client plstore
mastodon mastodon-transient tp mastodon-search mastodon-widget
mastodon-tl image-mode exif url-cache mastodon-toot multisession sqlite
facemenu mastodon-iso mastodon-http url-http url-auth url-gw link-hint
ffap goto-addr avy gnus-fun highlight-quoted corfu-history
corfu-popupinfo corfu evil-embrace embrace expand-region
text-mode-expansions the-org-mode-expansions org-element org-persist xdg
org-id org-refile org-element-ast inline avl-tree generator
er-basic-expansions expand-region-core expand-region-custom
evil-surround shr-color mm-archive qp sort smiley gnus-cite mail-extr
textsec uni-scripts idna-mapping ucs-normalize uni-confusable
textsec-check gnus-async gnus-bcklg visual-wrap gnus-ml disp-table
nndraft nnmh vertico-repeat epa-file network-stream nsm gnus-agent
gnus-srvr gnus-score score-mode nnvirtual nntp gnus-cache server mu4e
mu4e-org org ob ob-tangle ob-ref ob-lob ob-table ob-exp org-macro
org-src sh-script smie treesit executable ob-comint org-pcomplete
pcomplete org-list org-footnote org-faces org-entities noutline outline
org-version ob-emacs-lisp ob-core ob-eval org-cycle org-table ol
org-fold org-fold-core org-keys oc org-loaddefs find-func org-compat
org-macs mu4e-notification notifications mu4e-main smtpmail mu4e-view
mu4e-mime-parts crm cal-menu calendar cal-loaddefs mu4e-headers
mu4e-thread mu4e-actions mu4e-compose mu4e-draft gnus-msg gnus-art mm-uu
mml2015 mm-view mml-smime smime gnutls dig gnus-sum gnus-group gnus-undo
gnus-start gnus-dbus dbus gnus-cloud nnimap nnmail mail-source utf7 nnoo
parse-time iso8601 gnus-spec gnus-int gnus-range gnus-win gnus nnheader
range mu4e-search mu4e-lists mu4e-bookmarks mu4e-mark mu4e-message shr
pixel-fill kinsoku url-file svg xml dom browse-url flow-fill mule-util
hl-line mu4e-contacts mu4e-update mu4e-folders mu4e-context
mu4e-query-items mu4e-server mu4e-modeline mu4e-vars mu4e-helpers
mu4e-config mu4e-window ido message sendmail yank-media puny dirvish
transient format-spec cus-start autorevert filenotify dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util time-date mm-decode mm-bodies mm-encode mail-parse rfc2231
rfc2047 rfc2045 mm-util ietf-drums mail-prsvr mailabbrev mail-utils
gmm-utils mailheader mu4e-obsolete usher satchel ibuf-ext ibuffer
ibuffer-loaddefs bookmark nerd-icons-completion nerd-icons
nerd-icons-faces nerd-icons-data nerd-icons-data-mdicon
nerd-icons-data-flicon nerd-icons-data-codicon nerd-icons-data-devicon
nerd-icons-data-sucicon nerd-icons-data-wicon nerd-icons-data-faicon
nerd-icons-data-powerline nerd-icons-data-octicon
nerd-icons-data-pomicon nerd-icons-data-ipsicon marginalia
vertico-multiform vertico evil-goggles pulse color ef-spring-theme
ef-themes pdf-loader anzu advice evil evil-integration evil-maps
evil-commands reveal evil-jumps evil-command-window evil-types
evil-search evil-ex evil-macros evil-repeat evil-states evil-core comp
comp-cstr cl-extra help-mode comp-run comp-common evil-common rect
evil-vars edmacro kmacro orderless compat mastodon-autoloads
tp-autoloads yeetube-autoloads filechooser-autoloads wile-autoloads
usher-autoloads undo-fu-session-autoloads ef-themes-autoloads
jit-spell-autoloads satchel-autoloads pdf-tools-autoloads
tablist-autoloads xr-autoloads link-hint-autoloads avy-autoloads
yaml-mode-autoloads markdown-mode-autoloads magit-autoloads
magit-section-autoloads llama-autoloads inspirehep-autoloads
consult-hoogle-autoloads haskell-ng-mode-autoloads
geiser-guile-autoloads geiser-autoloads fabs-autoloads
companion-mode-autoloads eww-hacks-autoloads evil-embrace-autoloads
embrace-autoloads expand-region-autoloads evil-surround-autoloads
evil-nerd-commenter-autoloads evil-goggles-autoloads anzu-autoloads
evil-autoloads goto-chg-autoloads eshell-syntax-highlighting-autoloads
eat-autoloads package-lint-flymake-autoloads package-lint-autoloads
macrostep-autoloads helpful-autoloads dash-autoloads
highlight-quoted-autoloads dirvish-autoloads transient-autoloads
diredfl-autoloads trashed-autoloads vertico-autoloads
orderless-autoloads nerd-icons-completion-autoloads marginalia-autoloads
embark-consult-autoloads embark-autoloads nerd-icons-corfu-autoloads
nerd-icons-autoloads cape-autoloads corfu-autoloads consult-autoloads
password-store-autoloads with-editor-autoloads elpaca-log elpaca-ui
recentf tree-widget savehist saveplace elec-pair bind-key easy-mmode
pcase ibuf-macs elpaca-menu-elpa flymake project compile
text-property-search comint ansi-osc ansi-color warnings thingatpt
pixel-scroll cua-base ring repeat rx midnight cus-edit pp cus-load
wid-edit elpaca-menu-melpa url url-proxy url-privacy url-expand
url-methods url-history url-cookie generate-lisp-file url-domsuf
url-util url-parse auth-source eieio eieio-core cl-macs icons
password-cache json subr-x map byte-opt gv bytecomp byte-compile
url-vars mailcap elpaca-menu-org cl-seq elpaca elpaca-process
cl-loaddefs cl-lib elpaca-autoloads rmc iso-transl tooltip cconv eldoc
paren electric uniquify ediff-hook vc-hooks lisp-float-type elisp-mode
mwheel term/pgtk-win pgtk-win term/common-win touch-screen pgtk-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list replace
newcomment text-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch easymenu timer select scroll-bar mouse jit-lock
font-lock syntax font-core term/tty-colors frame minibuffer nadvice seq
simple cl-generic indonesian philippine cham georgian utf-8-lang
misc-lang vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms
cp51932 hebrew greek romanian slovak czech european ethiopic indian
cyrillic chinese composite emoji-zwj charscript charprop case-table
epa-hook jka-cmpr-hook help abbrev obarray oclosure cl-preloaded button
loaddefs theme-loaddefs faces cus-face macroexp files window
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget keymap hashtable-print-readable backquote threads dbusbind
inotify dynamic-setting system-font-setting font-render-setting cairo
gtk pgtk multi-tty move-toolbar make-network-process native-compile
emacs)

Memory information:
((conses 16 2800767 1018448) (symbols 48 55579 14)
 (strings 32 442784 42118) (string-bytes 1 104419678)
 (vectors 16 142884) (vector-slots 8 2364850 481311)
 (floats 8 2309 34438) (intervals 56 224392 6429) (buffers 992 81))