Subject: 30.1; Performance issue with inline image in an org file

From: laladrik <[email-masked]>
Date: Mon, 09 Jun 2025 19:23:56 +0000

Hello,

I've been experiencing performance issues with displaying inline images in Org
files.  This happens only if an image is resized.  Emacs stops being responsive
and its CPU core gets busy to 100%.  There 3 ways to reproduce it:

1. Put the mouse cursor on an image and put the caret on some text. Select some
text with the keyboard.  The process of the selection is like a slide show.
3.1. Open an Org file with some text and at least one inline image.
1.2. Put the caret on first line.
1.3. Put the mouse cursor on the image.
1.4. Press shift + space to start the selection.
1.5. Press down to select 10 or 20 lines. You can press up and down to change
the selection if you haven't got the lagging from the first time.
2. Scroll the document.  Emacs slows down when an image appears under the
cursor. Details:
2.1. Open an Org file which long enough for scrolling and has inline images.
2.2. Make sure that inline images are displayed and big enough to be resized by
Emacs.
2.3. Scroll the document with the mouse cursor at any position, where it might
be over an image.
2.4. When the mouse cursor is over an image, I see that scrolling lags.
3. Move the mouse cursor on and off the image.  The picture of the cursor
changes with a lag.
3.1. Open an Org file with some text and at least one inline image.
3.2. Make sure that inline images are displayed and big enough to be resized by
Emacs.
3.3. Move the mouse cursor on and off the image.
3.4. When the mouse cursor crosses the edge of the image, the picture of the
mouse cursor changes with some lag.

The init.el needed to reproduce has just one line:

    (setq org-image-actual-width 1000)

The value 1000 can be replaced with any - no difference. After searching some
information on the internet I found that the problem can be cause by frequent
garbage collection.  As a result I added this line:

    (setq gc-cons-threshold (* 50 1000 1000))

It doesn't help, but for now I run into the performance issue with just these
two lines in the init.el anyway.

The Emacs CPU profiler shows this:

       13131  95% - redisplay_internal (C function)
           2   0%  - eval
           1   0%   - if
           1   0%      frame-parameter
           1   0%     mode-line-eol-desc
           1   0%  - redisplay--pre-redisplay-functions
           1   0%   - run-hook-with-args
           1   0%    - redisplay--update-region-highlight
           1   0%       redisplay--highlight-overlay-function
         616   4% - command-execute
         566   4%  - byte-code
         566   4%   - read-extended-command
         566   4%    - read-extended-command-1
         566   4%     - completing-read-default
         395   2%        redisplay_internal (C function)
         170   1%      - command-execute
         170   1%       - funcall-interactively
         170   1%        - minibuffer-complete
         170   1%         - completion-in-region
         170   1%          - completion--in-region
         170   1%           + #<byte-code-function 429>
          50   0%  + funcall-interactively
          23   0% + timer-event-handler
           3   0% + #<byte-code-function E5A>
           2   0% + gui-set-selection
           1   0% + internal-timer-start-idle
           0   0%   ...

The function redisplay_internal takes 95%.

I built Emacs with debug symbols to profile it with Linux Perf.  The
output of perf stat:

    Performance counter stats for process id '76585,76586':

            16,656.41 msec task-clock:u                     #    0.913 CPUs utilized
                    0      context-switches:u               #    0.000 /sec
                    0      cpu-migrations:u                 #    0.000 /sec
                1,254      page-faults:u                    #   75.286 /sec
       62,738,001,762      cycles:u                         #    3.767 GHz                         (49.97%)
          941,864,008      stalled-cycles-frontend:u        #    1.50% frontend cycles idle        (50.01%)
      268,065,500,133      instructions:u                   #    4.27  insn per cycle
                                                     #    0.00  stalled cycles per insn     (50.04%)
       31,137,526,550      branches:u                       #    1.869 G/sec                       (50.02%)
           71,463,191      branch-misses:u                  #    0.23% of all branches             (49.95%)

         18.251526038 seconds time elapsed

I made a record with Linux perf, but the output doesn't make sense to me.
The function taking the CPU time is:

    bits_image_fetch_separable_convolution_affine_none_x8r8g8b8

It's defined in libpixman.  The problem is that the report doesn't show the
callstack to the function.  As a result, I can't say what calls the function in
Emacs.  I can upload the Linux Perf file somewhere and give a link to it.

Here is my system configuration:

    System:
      Kernel: 6.12.9-100.fc40.x86_64 arch: x86_64 bits: 64
      Desktop: i3 v: 4.24 Distro: Fedora Linux 40 (Workstation Edition)
    CPU:
      Info: 8-core model: AMD Ryzen 7 3800X bits: 64 type: MT MCP cache: L2: 4 MiB
      Speed (MHz): min/max: 2200/4559
    Graphics:
      Device-1: NVIDIA TU104 [GeForce RTX 2060] driver: nvidia v: 565.77
      API: OpenGL v: 4.6.0 vendor: nvidia v: 565.77 renderer: NVIDIA GeForce
    Info:
      Memory: total: 64 GiB

I come across the issue with 2 versions of Emacs.  29.4 and 30.1.  However, all
the data, I'm providing is from the version 30.1, because I compiled it with
debug symbols.

I experience it with a few Org files and pictures.  However, I have two examples
I can share.  I'll give the links to them on request to minimize the chance of
my message being considered as a spam.

I'm happy to provide more details if needed.  Also, I had a discussion on
stackoverflow about it.  The discussion doesn't have more information, but I
can provide a link to it as well on request.

About sending this bug report.  I couldn't figure out how to send a bug report
via Emacs itself.  As a result, I prepared the text of the template for the
message from Emacs only.  The message is sent from an email client manually.
Given that, the bug report might miss some information, which is sent regularly
via Emacs.  I'm happy to give you the information if you tell me how.

Best regards,
Aleksei Markov

In GNU Emacs 30.1 (build 1, x86_64-pc-linux-gnu, X toolkit, cairo
 version 1.18.2, Xaw3d scroll bars) of 2025-06-06 built on raven
Windowing system distributor 'The X.Org Foundation', version 11.0.12014000
System Description: Fedora Linux 40 (Workstation Edition)

Configured using:
 'configure 'CFLAGS=-g -O2''

Configured features:
CAIRO DBUS FREETYPE GIF GLIB GMP GNUTLS GPM GSETTINGS HARFBUZZ JPEG
LIBOTF LIBSELINUX LIBSYSTEMD LIBXML2 M17N_FLT MODULES NATIVE_COMP NOTIFY
INOTIFY PDUMPER PNG RSVG SECCOMP SOUND SQLITE3 THREADS TIFF
TOOLKIT_SCROLL_BARS TREE_SITTER WEBP X11 XAW3D XDBE XIM XINPUT2 XPM
LUCID ZLIB

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

Major mode: Fundamental

Minor modes in effect:
  tooltip-mode: t
  global-eldoc-mode: t
  show-paren-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  tool-bar-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  blink-cursor-mode: t
  minibuffer-regexp-mode: t
  buffer-read-only: t
  line-number-mode: t
  indent-tabs-mode: t
  transient-mark-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t

Load-path shadows:
None found.

Features:
(shadow sort mail-extr emacsbug message yank-media puny dired
dired-loaddefs rfc822 mml mml-sec epa derived epg rfc6068 epg-config
gnus-util text-property-search time-date mm-decode mm-bodies mm-encode
mail-parse rfc2231 mailabbrev gmm-utils mailheader sendmail rfc2047
rfc2045 ietf-drums mm-util mail-prsvr mail-utils info evil-autoloads
goto-chg-autoloads package browse-url url url-proxy url-privacy
url-expand url-methods url-history url-cookie generate-lisp-file
url-domsuf url-util mailcap url-handlers url-parse auth-source cl-seq
eieio eieio-core cl-macs icons password-cache json subr-x map byte-opt
gv bytecomp byte-compile url-vars cl-loaddefs cl-lib rmc iso-transl
tooltip cconv eldoc paren electric uniquify ediff-hook vc-hooks
lisp-float-type elisp-mode mwheel term/x-win x-win term/common-win x-dnd
touch-screen 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 x-toolkit xinput2 x multi-tty move-toolbar
make-network-process native-compile emacs)

Memory information:
((conses 16 87385 64353) (symbols 48 7825 2) (strings 32 24929 9977)
 (string-bytes 1 841110) (vectors 16 14718)
 (vector-slots 8 190273 44458) (floats 8 34 10) (intervals 56 388 263)
 (buffers 992 11)