all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* bug#62352: Very slow scroll-down-line with a lot of text properties
@ 2023-03-21 20:01 Herman, Geza
  2023-03-21 20:26 ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Geza @ 2023-03-21 20:01 UTC (permalink / raw)
  To: 62352

[-- Attachment #1: Type: text/plain, Size: 8528 bytes --]

Emacs can freeze for seconds when scroll-down-line is called multiple 
times if the buffer contains a lot of text properties.

I noticed this when using lsp-mode for C++.

To reproduce the issue without lsp-mode, execute this elisp command with 
M-:, this will add a face to operators:

(font-lock-add-keywords 'c++-mode '(("[][~!^&\|<>:=,.\\+*/%-]" 0 'error)))

Also, bind scroll-down-line to a key, like shift-up:

(global-set-key (kbd "<S-up>") 'scroll-down-line)

Then, put the attached example.cpp into a c++ buffer, move the point to 
the bottom, then press and hold shift-up. Emacs will update the window 
for several scroll events, but after that it won't update the screen for 
a while. Even after shift-up is released, Emacs needs several seconds to 
be interactive again.


In GNU Emacs 29.0.60 (build 1, x86_64-pc-linux-gnu, GTK+ Version
  3.24.36, cairo version 1.16.0) of 2023-03-21 built on okoska
Repository revision: 7a1272168af1a5b82979efa29451147c5d867981
Repository branch: emacs-29
Windowing system distributor 'The X.Org Foundation', version 11.0.12101006
System Description: Debian GNU/Linux bookworm/sid

Configured using:
  'configure --with-native-compilation=aot --without-compress-install
  --with-json --with-xinput2 --with-xwidgets --with-tree-sitter
  --with-cairo'

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

Important settings:
   value of $LC_ALL: C.UTF-8
   value of $LANG: en_US.UTF-8
   value of $XMODIFIERS: @im=none
   locale-coding-system: utf-8-unix

Major mode: C++//l

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
   font-lock-mode: t
   blink-cursor-mode: 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
   abbrev-mode: t

Load-path shadows:
/home/geza/.emacs.d/elpa/transient-20230304.1149/transient hides 
/usr/local/share/emacs/29.0.60/lisp/transient

Features:
(shadow sort mail-extr emacsbug message mailcap yank-media puny dired
dired-loaddefs rfc822 mml mml-sec password-cache 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 comp comp-cstr
warnings icons subr-x rx cl-seq cl-macs gv cl-extra help-mode bytecomp
byte-compile cc-mode cc-fonts cc-guess cc-menus cc-cmds cc-styles
cc-align cc-engine cc-vars cc-defs cl-loaddefs cl-lib info
evil-surround-autoloads scad-mode-autoloads qml-mode-autoloads
evil-exchange-autoloads evil-args-autoloads cmake-font-lock-autoloads
visual-fill-column-autoloads evil-textobj-anyblock-autoloads
evil-visualstar-autoloads impatient-mode-autoloads
volatile-highlights-autoloads maxima-autoloads
evil-cleverparens-autoloads consult-dir-autoloads gnuplot-autoloads
helpful-autoloads git-modes-autoloads font-lock-profiler-autoloads
paredit-autoloads math-preview-autoloads modern-cpp-font-lock-autoloads
column-enforce-mode-autoloads plantuml-mode-autoloads
yaml-mode-autoloads magit-todos-autoloads hl-todo-autoloads
treemacs-evil-autoloads define-word-autoloads paradox-autoloads
highlight-quoted-autoloads rg-autoloads lsp-pyright-autoloads
peep-dired-autoloads highlight-numbers-autoloads dired-filter-autoloads
fold-this-autoloads eterm-256color-autoloads xterm-color-autoloads
rainbow-mode-autoloads evil-org-autoloads evil-mc-autoloads
doom-modeline-autoloads hungry-delete-autoloads vterm-autoloads
magit-tbdiff-autoloads embark-consult-autoloads
literate-calc-mode-autoloads free-keys-autoloads memory-usage-autoloads
ccls-autoloads svg-tag-mode-autoloads atomic-chrome-autoloads
websocket-autoloads test-simple-autoloads frog-jump-buffer-autoloads
frog-menu-autoloads gcmh-autoloads elisp-refs-autoloads
evil-textobj-line-autoloads yasnippet-autoloads
highlight-doxygen-autoloads vertico-autoloads elfeed-autoloads
consult-lsp-autoloads dired-narrow-autoloads highlight-autoloads
smeargle-autoloads clean-aindent-mode-autoloads cmake-mode-autoloads
string-inflection-autoloads ws-butler-autoloads ov-autoloads
gif-screencast-autoloads diredfl-autoloads go-mode-autoloads
centered-cursor-mode-autoloads lsp-ui-autoloads camcorder-autoloads
easy-mmode org-jira-autoloads ag-autoloads queue-autoloads
orderless-autoloads hide-lines-autoloads git-timemachine-autoloads
csv-mode-autoloads broadcast-autoloads evil-leader-autoloads
hide-mode-line-autoloads company-box-autoloads transpose-frame-autoloads
lsp-treemacs-autoloads evil-textobj-entire-autoloads htmlize-autoloads
dired-subtree-autoloads dired-hacks-utils-autoloads
evil-multiedit-autoloads iedit-autoloads orgit-autoloads
evil-textobj-column-autoloads names-autoloads advice wgrep-autoloads
ninja-mode-autoloads smartparens-autoloads powerthesaurus-autoloads
request-autoloads git-gutter-fringe-autoloads git-gutter-autoloads
highlight-indent-guides-autoloads parent-mode-autoloads
pcre2el-autoloads rust-mode-autoloads evil-snipe-autoloads
fringe-helper-autoloads glsl-mode-autoloads disk-usage-autoloads
highlight-symbol-autoloads git-identity-autoloads
consult-flycheck-autoloads consult-autoloads shrink-path-autoloads
treemacs-projectile-autoloads winum-autoloads svg-lib-autoloads
org-superstar-autoloads rmsbolt-autoloads dired-git-info-autoloads
all-the-icons-autoloads ob-ipython-autoloads dash-functional-autoloads
evil-collection-autoloads annalist-autoloads treemacs-magit-autoloads
treemacs-autoloads cfrs-autoloads pfuture-autoloads ace-window-autoloads
avy-autoloads demangle-mode-autoloads frame-local-autoloads
evil-anzu-autoloads anzu-autoloads evil-indent-plus-autoloads
better-jumper-autoloads delight-autoloads company-autoloads
projectile-autoloads marginalia-autoloads dumb-jump-autoloads
popup-autoloads bm-autoloads vdiff-magit-autoloads vdiff-autoloads
hydra-autoloads evil-lion-autoloads evil-autoloads goto-chg-autoloads
evil-matchit-autoloads async-autoloads simple-httpd-autoloads
lorem-ipsum-autoloads expand-region-autoloads shut-up-autoloads
flycheck-autoloads pkg-info-autoloads epl-autoloads
page-break-lines-autoloads embark-autoloads which-key-autoloads
blamer-autoloads posframe-autoloads git-link-autoloads
drag-stuff-autoloads code-review-autoloads emojify-autoloads
forge-autoloads yaml-autoloads deferred-autoloads uuidgen-autoloads
ghub-autoloads treepy-autoloads a-autoloads magit-autoloads
magit-section-autoloads git-commit-autoloads with-editor-autoloads
transient-autoloads closql-autoloads emacsql-autoloads compat-autoloads
lsp-mode-autoloads lv-autoloads markdown-mode-autoloads
spinner-autoloads ht-autoloads f-autoloads s-autoloads dash-autoloads
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 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 xwidget-internal dbusbind inotify lcms2 dynamic-setting
system-font-setting font-render-setting cairo move-toolbar gtk x-toolkit
xinput2 x multi-tty make-network-process native-compile emacs)

Memory information:
((conses 16 150387 10063)
  (symbols 48 11460 0)
  (strings 32 32439 3583)
  (string-bytes 1 1406757)
  (vectors 16 19041)
  (vector-slots 8 386441 14177)
  (floats 8 38 102)
  (intervals 56 7894 0)
  (buffers 984 13))

[-- Attachment #2: example.cpp --]
[-- Type: text/x-c++src, Size: 9448 bytes --]

#include <array>

struct AffineMatrix {
    std::array<float, 12> c;
};

AffineMatrix foo(const AffineMatrix &a, const AffineMatrix &b) {
    AffineMatrix r;

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    r.c[0] = a.c[0] * b.c[0] + a.c[1] * b.c[4] + a.c[2] * b.c[ 8];
    r.c[1] = a.c[0] * b.c[1] + a.c[1] * b.c[5] + a.c[2] * b.c[ 9];
    r.c[2] = a.c[0] * b.c[2] + a.c[1] * b.c[6] + a.c[2] * b.c[10];
    r.c[3] = a.c[0] * b.c[3] + a.c[1] * b.c[7] + a.c[2] * b.c[11] + a.c[3];

    r.c[4] = a.c[4] * b.c[0] + a.c[5] * b.c[4] + a.c[6] * b.c[ 8];
    r.c[5] = a.c[4] * b.c[1] + a.c[5] * b.c[5] + a.c[6] * b.c[ 9];
    r.c[6] = a.c[4] * b.c[2] + a.c[5] * b.c[6] + a.c[6] * b.c[10];
    r.c[7] = a.c[4] * b.c[3] + a.c[5] * b.c[7] + a.c[6] * b.c[11] + a.c[7];

    r.c[ 8] = a.c[8] * b.c[0] + a.c[9] * b.c[4] + a.c[10] * b.c[ 8];
    r.c[ 9] = a.c[8] * b.c[1] + a.c[9] * b.c[5] + a.c[10] * b.c[ 9];
    r.c[10] = a.c[8] * b.c[2] + a.c[9] * b.c[6] + a.c[10] * b.c[10];
    r.c[11] = a.c[8] * b.c[3] + a.c[9] * b.c[7] + a.c[10] * b.c[11] + a.c[11];

    return r;
}

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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-21 20:01 bug#62352: Very slow scroll-down-line with a lot of text properties Herman, Geza
@ 2023-03-21 20:26 ` Eli Zaretskii
  2023-03-21 20:39   ` Herman, Géza
  2023-03-21 21:58   ` Gregory Heytings
  0 siblings, 2 replies; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-21 20:26 UTC (permalink / raw)
  To: Herman, Geza; +Cc: 62352

> Date: Tue, 21 Mar 2023 21:01:58 +0100
> From: "Herman, Geza" <geza.herman@gmail.com>
> 
> Emacs can freeze for seconds when scroll-down-line is called multiple 
> times if the buffer contains a lot of text properties.

Not any text properties: 'face' text properties.  Right?

> To reproduce the issue without lsp-mode, execute this elisp command with 
> M-:, this will add a face to operators:
> 
> (font-lock-add-keywords 'c++-mode '(("[][~!^&\|<>:=,.\\+*/%-]" 0 'error)))
> 
> Also, bind scroll-down-line to a key, like shift-up:
> 
> (global-set-key (kbd "<S-up>") 'scroll-down-line)
> 
> Then, put the attached example.cpp into a c++ buffer, move the point to 
> the bottom, then press and hold shift-up. Emacs will update the window 
> for several scroll events, but after that it won't update the screen for 
> a while. Even after shift-up is released, Emacs needs several seconds to 
> be interactive again.

In general, any change in faces causes the display iterator to stop
and load the new face, before it continues.  They also cause drawing
on the screen to be in smaller chunks, since each stretch of
characters in the same face is drawn together.  And this example
basically changes to a new face every character.  So this is expected
to display slower than usual.

However, are you saying that this is slower in Emacs 29 than it was in
Emacs 28?  If so, bisection will be appreciated.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-21 20:26 ` Eli Zaretskii
@ 2023-03-21 20:39   ` Herman, Géza
  2023-03-21 21:58   ` Gregory Heytings
  1 sibling, 0 replies; 16+ messages in thread
From: Herman, Géza @ 2023-03-21 20:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352



On 3/21/23 21:26, Eli Zaretskii wrote:
>> Date: Tue, 21 Mar 2023 21:01:58 +0100
>> From: "Herman, Geza" <geza.herman@gmail.com>
>>
>> Emacs can freeze for seconds when scroll-down-line is called multiple
>> times if the buffer contains a lot of text properties.
> Not any text properties: 'face' text properties.  Right?
Probably, I don't know how to check other properties.

> In general, any change in faces causes the display iterator to stop
> and load the new face, before it continues.  They also cause drawing
> on the screen to be in smaller chunks, since each stretch of
> characters in the same face is drawn together.  And this example
> basically changes to a new face every character.  So this is expected
> to display slower than usual.
>
> However, are you saying that this is slower in Emacs 29 than it was in
> Emacs 28?  If so, bisection will be appreciated.
I checked this with emacs 28 now, it behaves the same, so it's not a 
regression.

It seems this issue is scroll-down-line specific. I mean, it doesn't 
happen with the "normal" scrolling (when the point is also moved, not 
just the window start point). If I set scroll-conservatively to 101 
(this way normal scrolling behaves the same as scroll-down-line after 
the point hits the edge of the window), and move upwards with the cursor 
key, scrolling is more-or-less OK. It's a little bit erratic, but no 
freeze happens.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-21 20:26 ` Eli Zaretskii
  2023-03-21 20:39   ` Herman, Géza
@ 2023-03-21 21:58   ` Gregory Heytings
  2023-03-25 11:58     ` Eli Zaretskii
  1 sibling, 1 reply; 16+ messages in thread
From: Gregory Heytings @ 2023-03-21 21:58 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352, Herman, Geza


>
> However, are you saying that this is slower in Emacs 29 than it was in 
> Emacs 28?  If so, bisection will be appreciated.
>

FTR, I see the same behavior in Emacs 26, 27, 28 and 29 with that recipe.






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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-21 21:58   ` Gregory Heytings
@ 2023-03-25 11:58     ` Eli Zaretskii
  2023-03-25 12:33       ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-25 11:58 UTC (permalink / raw)
  To: Gregory Heytings; +Cc: 62352-done, geza.herman

> Date: Tue, 21 Mar 2023 21:58:05 +0000
> From: Gregory Heytings <gregory@heytings.org>
> cc: "Herman, Geza" <geza.herman@gmail.com>, 62352@debbugs.gnu.org
> 
> > However, are you saying that this is slower in Emacs 29 than it was in 
> > Emacs 28?  If so, bisection will be appreciated.
> 
> FTR, I see the same behavior in Emacs 26, 27, 28 and 29 with that recipe.

That figures: the relevant code didn't see any significant changes in
the recent years.

I also think the impression that C-n/C-p (with scroll-conservatively)
are free of this problem is inaccurate.  They are a bit faster,
indeed, but in my unoptimized build I see the scroll taking almost the
same time in both cases, close to 1 sec.  I think OP's impression is
based on where each of these crosses the threshold of Emacs being able
to keep up with the repeated keystrokes, and that depends on both the
auto-repeat rate and the CPU power, so it is different on different
systems.  E.g., in an optimized build I see no stuttering with
scroll-down-line, either.

So I think there's no bug here we need to look into, and I'm therefore
closing this bug report.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 11:58     ` Eli Zaretskii
@ 2023-03-25 12:33       ` Herman, Géza
  2023-03-25 12:42         ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Géza @ 2023-03-25 12:33 UTC (permalink / raw)
  To: Eli Zaretskii, Gregory Heytings; +Cc: 62352-done

There is a behavior difference. I'm not sure what the exact reason is, 
but the two different scrolls behave differently.

With a more complex case (~250 lines, each line is 150 characters long), 
I can scroll "normally" from the bottom to the top in ~5 seconds (with 
scroll-conservatively=101). The scrolling stutters. If I disable 
font-lock, it won't get any faster, still ~5 seconds (but fluid).

With scroll-down-line, emacs freezes for 40 seconds, and when it 
unfreezes, it didn't even reach the top.

I analyzed the issue little bit, the root cause of the slowdown is 
composition handling (yet the composition feature is completely unused 
by this example).

If I comment out these lines in composition_compute_stop_pos(), emacs 
works better ("normal" scrolling becomes completely fluid, 
scroll-down-line still freezes, but for a much shorter time):

   /* if (charpos < endpos */
   /*     && find_composition (charpos, endpos, &start, &end, &prop, 
string) */
   /*     && start >= charpos */
   /*     && composition_valid_p (start, end, prop)) */
   /*   { */
   /*     cmp_it->stop_pos = endpos = start; */
   /*     cmp_it->ch = -1; */
   /*   } */

It seems that emacs does a huge amount of redundant work by scanning 
approximately the same area over and over again for composition properties.

I'm not sure how it is possible that you don't see any stuttering with 
scroll-down-line. On my system it freezes for seconds with my original 
example (and others can also reproduce it).


On 3/25/23 12:58, Eli Zaretskii wrote:
>> Date: Tue, 21 Mar 2023 21:58:05 +0000
>> From: Gregory Heytings <gregory@heytings.org>
>> cc: "Herman, Geza" <geza.herman@gmail.com>, 62352@debbugs.gnu.org
>>
>>> However, are you saying that this is slower in Emacs 29 than it was in
>>> Emacs 28?  If so, bisection will be appreciated.
>> FTR, I see the same behavior in Emacs 26, 27, 28 and 29 with that recipe.
> That figures: the relevant code didn't see any significant changes in
> the recent years.
>
> I also think the impression that C-n/C-p (with scroll-conservatively)
> are free of this problem is inaccurate.  They are a bit faster,
> indeed, but in my unoptimized build I see the scroll taking almost the
> same time in both cases, close to 1 sec.  I think OP's impression is
> based on where each of these crosses the threshold of Emacs being able
> to keep up with the repeated keystrokes, and that depends on both the
> auto-repeat rate and the CPU power, so it is different on different
> systems.  E.g., in an optimized build I see no stuttering with
> scroll-down-line, either.
>
> So I think there's no bug here we need to look into, and I'm therefore
> closing this bug report.






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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 12:33       ` Herman, Géza
@ 2023-03-25 12:42         ` Eli Zaretskii
  2023-03-25 13:41           ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-25 12:42 UTC (permalink / raw)
  To: geza.herman; +Cc: gregory, 62352-done

> Date: Sat, 25 Mar 2023 13:33:53 +0100
> Cc: 62352-done@debbugs.gnu.org
> From: Herman, Géza <geza.herman@gmail.com>
> 
> There is a behavior difference. I'm not sure what the exact reason is, 
> but the two different scrolls behave differently.
> 
> With a more complex case (~250 lines, each line is 150 characters long), 
> I can scroll "normally" from the bottom to the top in ~5 seconds (with 
> scroll-conservatively=101). The scrolling stutters. If I disable 
> font-lock, it won't get any faster, still ~5 seconds (but fluid).

Like I said: the threshold where it starts stuttering depends on many
local factors.  Basically, the code does a very similar job, except
that in one case it needs to determine the window's starting position,
while in the other it doesn't (so the jobs the code does are somewhat
different).

> I analyzed the issue little bit, the root cause of the slowdown is 
> composition handling (yet the composition feature is completely unused 
> by this example).
> 
> If I comment out these lines in composition_compute_stop_pos(), emacs 
> works better ("normal" scrolling becomes completely fluid, 
> scroll-down-line still freezes, but for a much shorter time):
> 
>    /* if (charpos < endpos */
>    /*     && find_composition (charpos, endpos, &start, &end, &prop, 
> string) */
>    /*     && start >= charpos */
>    /*     && composition_valid_p (start, end, prop)) */
>    /*   { */
>    /*     cmp_it->stop_pos = endpos = start; */
>    /*     cmp_it->ch = -1; */
>    /*   } */
> 
> It seems that emacs does a huge amount of redundant work by scanning 
> approximately the same area over and over again for composition properties.

This is a non-starter, unfortunately: the display engine _must_
support character composition, or else some scripts will display
incorrectly, and some features (like prettify-symbols-mode) will stop
working.

If you never need to display scripts which require character
composition, turn off auto-composition-mode in your local
configuration (but then expect incorrect display with some
characters).

> I'm not sure how it is possible that you don't see any stuttering with 
> scroll-down-line. On my system it freezes for seconds with my original 
> example (and others can also reproduce it).

Get a faster computer, or make your keyboard auto-repeat rate lower?





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 12:42         ` Eli Zaretskii
@ 2023-03-25 13:41           ` Herman, Géza
  2023-03-25 14:02             ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Géza @ 2023-03-25 13:41 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gregory, 62352-done



On 3/25/23 13:42, Eli Zaretskii wrote:
>> I analyzed the issue little bit, the root cause of the slowdown is
>> composition handling (yet the composition feature is completely unused
>> by this example).
>>
>> If I comment out these lines in composition_compute_stop_pos(), emacs
>> works better ("normal" scrolling becomes completely fluid,
>> scroll-down-line still freezes, but for a much shorter time):
>>
>>     /* if (charpos < endpos */
>>     /*     && find_composition (charpos, endpos, &start, &end, &prop,
>> string) */
>>     /*     && start >= charpos */
>>     /*     && composition_valid_p (start, end, prop)) */
>>     /*   { */
>>     /*     cmp_it->stop_pos = endpos = start; */
>>     /*     cmp_it->ch = -1; */
>>     /*   } */
>>
>> It seems that emacs does a huge amount of redundant work by scanning
>> approximately the same area over and over again for composition properties.
> This is a non-starter, unfortunately: the display engine _must_
> support character composition, or else some scripts will display
> incorrectly, and some features (like prettify-symbols-mode) will stop
> working.
I'm not saying that this is the solution. I just wanted to point out 
that emacs does unnecessary work. Note, even with this code commented 
out, composition still seems to work. Maybe it's buggy, of course. But 
the point is, it seems possible to fix this performance issue. Maybe 
it's a lot of work and it doesn't worth it. I understand that if this 
issue is closed because of this. But saying that this issue is not a 
(performance) bug is not correct, in my opinion. It is not unreasonable 
to expect that my example file can be scrolled without any problem.

> Get a faster computer, or make your keyboard auto-repeat rate lower?
Maybe there is a 2x single-thread performance factor between my computer 
and a current fast consumer desktop PC. It is highly unlikely that 
getting a faster computer will solve this problem.






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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 13:41           ` Herman, Géza
@ 2023-03-25 14:02             ` Eli Zaretskii
  2023-03-25 15:24               ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-25 14:02 UTC (permalink / raw)
  To: Herman, Géza; +Cc: 62352, gregory

> Date: Sat, 25 Mar 2023 14:41:04 +0100
> Cc: gregory@heytings.org, 62352-done@debbugs.gnu.org
> From: Herman, Géza <geza.herman@gmail.com>
> 
> >> If I comment out these lines in composition_compute_stop_pos(), emacs
> >> works better ("normal" scrolling becomes completely fluid,
> >> scroll-down-line still freezes, but for a much shorter time):
> >>
> >>     /* if (charpos < endpos */
> >>     /*     && find_composition (charpos, endpos, &start, &end, &prop,
> >> string) */
> >>     /*     && start >= charpos */
> >>     /*     && composition_valid_p (start, end, prop)) */
> >>     /*   { */
> >>     /*     cmp_it->stop_pos = endpos = start; */
> >>     /*     cmp_it->ch = -1; */
> >>     /*   } */
> >>
> >> It seems that emacs does a huge amount of redundant work by scanning
> >> approximately the same area over and over again for composition properties.
> > This is a non-starter, unfortunately: the display engine _must_
> > support character composition, or else some scripts will display
> > incorrectly, and some features (like prettify-symbols-mode) will stop
> > working.
> I'm not saying that this is the solution. I just wanted to point out 
> that emacs does unnecessary work.

How can Emacs know that this is "unnecessary work"?  You cannot know
if the buffer contains any composable characters unless you search for
them and fail to find them.

> Note, even with this code commented 
> out, composition still seems to work. Maybe it's buggy, of course.

If you comment out the call to find_composition, prettify-symbols-mode
will stop working, and likewise any other features that use the
'composition' text property.

> But the point is, it seems possible to fix this performance issue.

yes, it is possible to make the Emacs display faster by removing some
of the features it provides.  But that is hardly the right way of
"fixing" performance problems.

> But saying that this issue is not a (performance) bug is not
> correct, in my opinion.

That's not what I said, though.

> > Get a faster computer, or make your keyboard auto-repeat rate lower?
> Maybe there is a 2x single-thread performance factor between my computer 
> and a current fast consumer desktop PC. It is highly unlikely that 
> getting a faster computer will solve this problem.

My computer is a 10-year old machine.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 14:02             ` Eli Zaretskii
@ 2023-03-25 15:24               ` Herman, Géza
  2023-03-25 16:20                 ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Géza @ 2023-03-25 15:24 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352, gregory

[-- Attachment #1: Type: text/plain, Size: 3132 bytes --]



On 3/25/23 15:02, Eli Zaretskii wrote:
>>
>> I'm not saying that this is the solution. I just wanted to point out
>> that emacs does unnecessary work.
> How can Emacs know that this is "unnecessary work"?  You cannot know
> if the buffer contains any composable characters unless you search for
> them and fail to find them.
Properties don't appear out of thin air, emacs is the one who puts the 
properties to characters. So Emacs could build a parallel data structure 
which contains this information in a better searchable way. This data 
structure can be lazily constructed/updated, if it's needed.

Even, for my example problem, a simple 
"does-this-buffer-have-any-characters-with-composition-property" would 
be enough.

I'm not saying that this is the solution I want. I'm just saying this 
because you asked.

>> Note, even with this code commented
>> out, composition still seems to work. Maybe it's buggy, of course.
> If you comment out the call to find_composition, prettify-symbols-mode
> will stop working, and likewise any other features that use the
> 'composition' text property.
I commented out those lines I wrote, and I still see prettified symbols. 
I only commented out one find_composition call, all the other ones are 
still called. This call is bad, because it searches a 500-character 
area. A lot of other calls are fine because they only check one position 
(when limit<0).


>> But the point is, it seems possible to fix this performance issue.
> yes, it is possible to make the Emacs display faster by removing some
> of the features it provides.  But that is hardly the right way of
> "fixing" performance problems.
I didn't mean that. It can be made faster by using additional data 
structures/algorithms.
>> But saying that this issue is not a (performance) bug is not
>> correct, in my opinion.
> That's not what I said, though.
You said "So I think there's no bug here we need to look into". But I do 
think that there is a (performance) bug here.

>>> Get a faster computer, or make your keyboard auto-repeat rate lower?
>> Maybe there is a 2x single-thread performance factor between my computer
>> and a current fast consumer desktop PC. It is highly unlikely that
>> getting a faster computer will solve this problem.
> My computer is a 10-year old machine.
What is the conclusion then? You don't have this problem for some 
reason, or you do some part of the repro steps differently so it doesn't 
preproduce for you. Gregory Haythings could reproduce it, as far as I 
understand. So the problem is not at my side.

I attached scroll_problem.cpp, for which this problem is more apparent, 
maybe this reproduces for you. This is the file for which emacs freezed 
for 40 seconds, when I moved the point to the bottom, and pressed and 
hold Shift-up for 1-2 seconds.

For this file, if I intentionally prettify a lot of characters with 
"(setq prettify-symbols-alist '(("a" . ?λ)))", then the problem 
disappears, because emacs doesn't have to search for 500 characters to 
find nothing, but each search stops after a small amount of characters. 
For me, this shows that there is a problem.

[-- Attachment #2: scroll_problem.cpp --]
[-- Type: text/x-c++src, Size: 40261 bytes --]

void foo(float *a) {
    float x;

    x = a[1] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[2] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[3] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[4] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[5] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[6] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[7] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[8] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[9] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[10] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[11] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[12] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[13] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[14] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[15] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[16] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[17] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[18] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[19] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[20] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[21] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[22] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[23] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[24] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[25] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[26] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[27] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[28] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[29] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[30] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[31] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[32] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[33] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[34] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[35] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[36] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[37] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[38] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[39] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[40] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[41] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[42] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[43] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[44] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[45] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[46] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[47] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[48] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[49] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[50] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[51] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[52] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[53] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[54] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[55] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[56] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[57] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[58] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[59] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[60] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[61] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[62] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[63] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[64] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[65] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[66] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[67] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[68] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[69] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[70] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[71] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[72] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[73] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[74] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[75] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[76] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[77] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[78] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[79] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[80] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[81] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[82] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[83] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[84] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[85] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[86] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[87] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[88] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[89] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[90] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[91] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[92] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[93] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[94] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[95] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[96] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[97] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[98] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[99] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[100] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[101] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[102] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[103] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[104] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[105] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[106] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[107] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[108] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[109] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[110] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[111] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[112] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[113] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[114] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[115] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[116] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[117] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[118] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[119] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[120] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[121] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[122] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[123] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[124] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[125] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[126] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[127] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[128] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[129] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[130] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[131] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[132] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[1] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[2] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[3] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[4] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[5] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[6] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[7] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[8] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[9] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[10] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[11] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[12] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[13] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[14] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[15] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[16] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[17] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[18] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[19] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[20] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[21] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[22] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[23] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[24] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[25] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[26] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[27] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[28] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[29] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[30] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[31] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[32] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[33] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[34] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[35] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[36] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[37] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[38] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[39] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[40] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[41] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[42] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[43] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[44] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[45] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[46] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[47] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[48] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[49] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[50] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[51] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[52] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[53] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[54] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[55] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[56] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[57] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[58] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[59] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[60] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[61] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[62] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[63] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[64] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[65] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[66] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[67] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[68] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[69] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[70] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[71] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[72] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[73] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[74] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[75] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[76] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[77] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[78] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[79] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[80] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[81] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[82] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[83] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[84] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[85] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[86] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[87] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[88] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[89] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[90] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[91] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[92] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[93] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[94] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[95] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[96] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[97] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[98] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[99] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[100] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[101] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[102] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[103] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[104] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[105] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[106] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[107] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[108] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[109] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[110] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[111] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[112] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[113] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[114] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[115] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[116] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[117] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[118] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[119] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[120] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[121] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[122] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[123] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[124] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[125] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[126] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[127] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[128] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[129] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[130] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[131] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[132] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[1] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[2] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[3] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[4] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[5] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[6] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[7] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[8] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[9] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[10] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[11] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[12] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[13] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[14] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[15] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[16] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[17] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[18] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[19] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[20] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[21] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[22] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[23] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[24] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[25] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[26] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[27] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[28] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[29] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[30] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[31] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[32] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[33] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[34] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[35] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[36] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[37] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[38] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[39] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[40] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[41] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[42] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[43] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[44] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[45] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[46] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[47] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[48] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[49] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[50] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[51] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[52] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[53] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[54] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[55] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[56] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[57] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[58] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[59] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[60] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[61] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[62] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[63] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[64] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[65] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[66] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[67] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[68] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[69] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[70] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[71] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[72] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[73] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[74] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[75] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[76] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[77] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[78] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[79] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[80] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[81] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[82] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[83] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[84] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[85] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[86] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[87] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[88] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[89] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[90] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[91] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[92] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[93] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[94] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[95] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[96] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[97] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[98] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[99] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[100] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[101] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[102] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[103] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[104] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[105] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[106] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[107] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[108] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[109] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[110] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[111] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[112] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[113] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[114] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[115] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[116] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[117] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[118] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[119] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[120] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[121] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[122] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[123] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[124] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[125] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[126] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[127] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[128] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[129] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[130] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[131] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[132] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[1] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[2] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[3] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[4] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[5] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[6] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[7] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[8] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[9] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[10] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[11] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[12] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[13] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[14] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[15] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[16] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[17] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[18] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[19] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[20] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[21] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[22] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[23] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[24] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[25] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[26] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[27] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[28] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[29] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[30] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[31] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[32] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[33] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[34] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[35] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[36] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[37] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[38] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[39] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[40] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[41] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[42] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[43] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[44] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[45] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[46] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[47] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[48] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[49] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[50] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[51] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[52] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[53] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[54] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[55] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[56] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[57] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[58] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[59] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[60] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[61] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[62] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[63] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[64] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[65] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[66] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[67] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[68] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[69] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[70] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[71] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[72] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[73] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[74] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[75] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[76] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[77] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[78] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[79] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[80] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[81] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[82] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[83] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[84] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[85] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[86] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[87] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[88] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[89] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[90] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[91] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[92] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[93] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[94] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[95] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[96] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[97] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[98] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[99] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[100] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[101] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[102] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[103] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[104] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[105] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[106] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[107] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[108] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[109] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[110] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[111] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[112] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[113] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[114] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[115] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[116] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[117] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[118] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[119] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[120] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[121] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[122] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[123] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[124] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[125] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[126] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[127] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[128] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[129] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[130] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
    x = a[131] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1]; x = a[132] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1] * a[0] * a[1];
}

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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 15:24               ` Herman, Géza
@ 2023-03-25 16:20                 ` Eli Zaretskii
  2023-03-25 17:38                   ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-25 16:20 UTC (permalink / raw)
  To: Herman, Géza; +Cc: 62352, gregory

> Date: Sat, 25 Mar 2023 16:24:50 +0100
> Cc: gregory@heytings.org, 62352@debbugs.gnu.org
> From: Herman, Géza <geza.herman@gmail.com>
> 
> > How can Emacs know that this is "unnecessary work"?  You cannot know
> > if the buffer contains any composable characters unless you search for
> > them and fail to find them.
> Properties don't appear out of thin air, emacs is the one who puts the 
> properties to characters. So Emacs could build a parallel data structure 
> which contains this information in a better searchable way. This data 
> structure can be lazily constructed/updated, if it's needed.

It is not easy to maintain such a cache.  It needs to be kept up to
date at all times, and discarded/recreated when no longer accurate.
Any Lisp program can in principle add or remove text properties at any
moment, even inside a hook called by the display engine itself, such
as fontification-functions.

IOW, it is not as simple as you seem to think.

> Even, for my example problem, a simple 
> "does-this-buffer-have-any-characters-with-composition-property" would 
> be enough.

That would be a one-time condition.  It could become false before the
next redisplay cycle.  And checking this condition even once in a
large buffer with many text properties takes non-negligible time.

> I'm not saying that this is the solution I want. I'm just saying this 
> because you asked.

Please believe me when I say that such shortcuts aren't possible, at
least not in the simplistic way you are suggesting them.  There could
be some clever ideas that somehow do accomplish that, but I'd need to
see working code which implements them, not just ideas based on
simplifications of the real-life situations with which the Emacs
display code needs to cope in a production session.

It is no accident that any caches we do employ in the display code are
very localized and usually very short-lived; most are discarded when a
redisplay cycle ends.

> > That's not what I said, though.
> You said "So I think there's no bug here we need to look into". But I do 
> think that there is a (performance) bug here.

There's a performance problem, I agree.  But as long as the design of
the display engine is what it is, I don't see how that can be helped,
in a way that will cope much better with the massive amount of face
properties as in these examples.

> >>> Get a faster computer, or make your keyboard auto-repeat rate lower?
> >> Maybe there is a 2x single-thread performance factor between my computer
> >> and a current fast consumer desktop PC. It is highly unlikely that
> >> getting a faster computer will solve this problem.
> > My computer is a 10-year old machine.
> What is the conclusion then? You don't have this problem for some 
> reason, or you do some part of the repro steps differently so it doesn't 
> preproduce for you. Gregory Haythings could reproduce it, as far as I 
> understand. So the problem is not at my side.

I didn't say the problem is on your side, I just mentioned the factors
that could cause it be more severe on your system than on mine.

> I attached scroll_problem.cpp, for which this problem is more apparent, 
> maybe this reproduces for you.

Yes, it does.  But only in scroll-down-line, not in scroll-up-line.

> This is the file for which emacs freezed 
> for 40 seconds, when I moved the point to the bottom, and pressed and 
> hold Shift-up for 1-2 seconds.

Here it "freezes" for just 4 to 5 seconds, not 40.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 16:20                 ` Eli Zaretskii
@ 2023-03-25 17:38                   ` Herman, Géza
  2023-03-25 17:49                     ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Géza @ 2023-03-25 17:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352, gregory



On 3/25/23 17:20, Eli Zaretskii wrote:
>> Properties don't appear out of thin air, emacs is the one who puts the
>> properties to characters. So Emacs could build a parallel data structure
>> which contains this information in a better searchable way. This data
>> structure can be lazily constructed/updated, if it's needed.
> It is not easy to maintain such a cache.  It needs to be kept up to
> date at all times, and discarded/recreated when no longer accurate.
> Any Lisp program can in principle add or remove text properties at any
> moment, even inside a hook called by the display engine itself, such
> as fontification-functions.
>
> IOW, it is not as simple as you seem to think.
I didn't meant to imply that it is easy. It is certainly not. But, tbh, 
while emacs is fluid most of the time, it can be very stuttery 
sometimes. In my experience, this is usually caused by some lisp code. 
But when it isn't, it is usually caused by some code in this area. When 
I profile emacs, these functions (next_property_change and similar) are 
usually on the top of the list. So it would make sense to optimize 
around this area. Not just because of this issue, but in general.

I'm not necessarily suggesting a cache. Maybe it's better to actually 
always manage additional data structures. So, if a text property is 
added, it's not just set for the specific character area, but it will 
also modify search structures right away. So additional data structures 
were always in sync. Sure, it has some overhead. But if emacs does a lot 
of linear searches (and having a look at these functions, I see a lot of 
linear searches), this overhead will be quickly mitigated by the much 
faster searches. For example, if emacs had a list which only contained 
text segments with the composition property, the current 500-char area 
search will be much faster.

I'd definitely trade some CPU time in a way that emacs will use some 
more CPU time in general (to manage additional data structures), but it 
will never freeze (because corner cases are handled much better). But 
maybe that's just me, others have different opinions about this.

Anyways, this is not the best place to discuss this, and I also don't 
know too much about emacs's internals. My intuition is that these 
problems can be solved in faster way, that's all.
>> Even, for my example problem, a simple
>> "does-this-buffer-have-any-characters-with-composition-property" would
>> be enough.
> That would be a one-time condition.  It could become false before the
> next redisplay cycle.  And checking this condition even once in a
> large buffer with many text properties takes non-negligible time.

I'm not sure I understand. If a composition text property is added to a 
character, then emacs would increase a buffer-local counter (I mean a 
counter at the C side, not a buffer-local lisp variable). If a 
composition text property is removed from a character, then emacs would 
decrease the buffer-local counter. And of course there are other events 
that modifies the value of the counter (char copy, delete, etc.). So 
this counter would always contain the number of composition characters 
in a buffer, or in other words, whether a buffer has a composition char 
or not. So, if find_composition sees that this counter is zero, it can 
return right away. I'm sure that this extra check won't take any 
significant CPU time. Why wouldn't this work, why does the redisplay 
cycle break this idea?

(I'm not acutally suggesting this idea, of course, because it's just a 
bandaid instead of a proper solution. I'm just asking because I'm 
curious why this wouldn't work)

> There's a performance problem, I agree.  But as long as the design of
> the display engine is what it is, I don't see how that can be helped,
> in a way that will cope much better with the massive amount of face
> properties as in these examples.
Yeah, I understand this. This is what I meant in one of my previous 
comments: if this is a large work, then it makes sense to close this bug 
report, because maybe it's not worth fixing this. Too much work for a 
small gain.

>> This is the file for which emacs freezed
>> for 40 seconds, when I moved the point to the bottom, and pressed and
>> hold Shift-up for 1-2 seconds.
> Here it "freezes" for just 4 to 5 seconds, not 40.
Ok, cool, at least you can reproduce the problem now. I'm not sure what 
causes the large difference (4/5 sec vs. 40 sec). Maybe window size, or 
something else. But it doesn't matter too much. The point is, that for 
some buffers, scroll-down-line can cause some freezing.

(In my real life code, emacs freezes for about a second, so it's not too 
bad. But it's annoying if I hit by this too often, that's why I reported 
this issue)





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 17:38                   ` Herman, Géza
@ 2023-03-25 17:49                     ` Eli Zaretskii
  2023-03-25 21:39                       ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-25 17:49 UTC (permalink / raw)
  To: geza.herman; +Cc: 62352, gregory

> Date: Sat, 25 Mar 2023 18:38:19 +0100
> Cc: gregory@heytings.org, 62352@debbugs.gnu.org
> From: Herman, Géza <geza.herman@gmail.com>
> 
> > IOW, it is not as simple as you seem to think.
> I didn't meant to imply that it is easy. It is certainly not. But, tbh, 
> while emacs is fluid most of the time, it can be very stuttery 
> sometimes. In my experience, this is usually caused by some lisp code. 
> But when it isn't, it is usually caused by some code in this area. When 
> I profile emacs, these functions (next_property_change and similar) are 
> usually on the top of the list. So it would make sense to optimize 
> around this area. Not just because of this issue, but in general.
> 
> I'm not necessarily suggesting a cache. Maybe it's better to actually 
> always manage additional data structures. So, if a text property is 
> added, it's not just set for the specific character area, but it will 
> also modify search structures right away. So additional data structures 
> were always in sync. Sure, it has some overhead. But if emacs does a lot 
> of linear searches (and having a look at these functions, I see a lot of 
> linear searches), this overhead will be quickly mitigated by the much 
> faster searches. For example, if emacs had a list which only contained 
> text segments with the composition property, the current 500-char area 
> search will be much faster.

Emacs already handles text properties using an efficient data
structure, see intervals.c.  Feel free to suggest improvements to the
algorithms we use there.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 17:49                     ` Eli Zaretskii
@ 2023-03-25 21:39                       ` Herman, Géza
  2023-03-26  4:55                         ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Herman, Géza @ 2023-03-25 21:39 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352, gregory



On 3/25/23 18:49, Eli Zaretskii wrote:
>> Date: Sat, 25 Mar 2023 18:38:19 +0100
>> Cc: gregory@heytings.org, 62352@debbugs.gnu.org
>> From: Herman, Géza <geza.herman@gmail.com>
>>
>>> IOW, it is not as simple as you seem to think.
>> I didn't meant to imply that it is easy. It is certainly not. But, tbh,
>> while emacs is fluid most of the time, it can be very stuttery
>> sometimes. In my experience, this is usually caused by some lisp code.
>> But when it isn't, it is usually caused by some code in this area. When
>> I profile emacs, these functions (next_property_change and similar) are
>> usually on the top of the list. So it would make sense to optimize
>> around this area. Not just because of this issue, but in general.
>>
>> I'm not necessarily suggesting a cache. Maybe it's better to actually
>> always manage additional data structures. So, if a text property is
>> added, it's not just set for the specific character area, but it will
>> also modify search structures right away. So additional data structures
>> were always in sync. Sure, it has some overhead. But if emacs does a lot
>> of linear searches (and having a look at these functions, I see a lot of
>> linear searches), this overhead will be quickly mitigated by the much
>> faster searches. For example, if emacs had a list which only contained
>> text segments with the composition property, the current 500-char area
>> search will be much faster.
> Emacs already handles text properties using an efficient data
> structure, see intervals.c.  Feel free to suggest improvements to the
> algorithms we use there.
The problem is not there. The problem is that find_composition is only 
interested in the composition property, yet it scans all the properties 
linearly. And it scans it for 500 characters. This file has a lot of 
properties, this means a lot of unnecessary and duplicated work (because 
it does this for each character displayed, or something like this). If 
the composition property had its own list, then this problem wouldn't exist.

Anyways, I commented out those lines, the problem is gone, and 
everything still seems to work. I'll report back if I find some problems 
with it.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-25 21:39                       ` Herman, Géza
@ 2023-03-26  4:55                         ` Eli Zaretskii
  2023-03-26  7:14                           ` Herman, Géza
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2023-03-26  4:55 UTC (permalink / raw)
  To: geza.herman; +Cc: 62352, gregory

> Date: Sat, 25 Mar 2023 22:39:34 +0100
> Cc: gregory@heytings.org, 62352@debbugs.gnu.org
> From: Herman, Géza <geza.herman@gmail.com>
> 
> >> I'm not necessarily suggesting a cache. Maybe it's better to actually
> >> always manage additional data structures. So, if a text property is
> >> added, it's not just set for the specific character area, but it will
> >> also modify search structures right away. So additional data structures
> >> were always in sync. Sure, it has some overhead. But if emacs does a lot
> >> of linear searches (and having a look at these functions, I see a lot of
> >> linear searches), this overhead will be quickly mitigated by the much
> >> faster searches. For example, if emacs had a list which only contained
> >> text segments with the composition property, the current 500-char area
> >> search will be much faster.
> > Emacs already handles text properties using an efficient data
> > structure, see intervals.c.  Feel free to suggest improvements to the
> > algorithms we use there.
> The problem is not there. The problem is that find_composition is only 
> interested in the composition property, yet it scans all the properties 
> linearly.

It doesn't scan linearly.  It calls next-single-property-change, which
traverses the interval tree we use for storing text properties.
Please take a look at the implementation of
next-single-property-change in textprop.c.

> And it scans it for 500 characters. This file has a lot of 
> properties, this means a lot of unnecessary and duplicated work (because 
> it does this for each character displayed, or something like this). If 
> the composition property had its own list, then this problem wouldn't exist.

If the composition property had its own data structure, Emacs would
need to search them both when it looks for a change in _any_ property
(something that happens quite a lot in other places), and handle
various combinations of hits in both data structures.  That'd be a
significant complication for a small gain.





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

* bug#62352: Very slow scroll-down-line with a lot of text properties
  2023-03-26  4:55                         ` Eli Zaretskii
@ 2023-03-26  7:14                           ` Herman, Géza
  0 siblings, 0 replies; 16+ messages in thread
From: Herman, Géza @ 2023-03-26  7:14 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 62352, gregory


On 3/26/23 06:55, Eli Zaretskii wrote:
>>
>> The problem is not there. The problem is that find_composition is only
>> interested in the composition property, yet it scans all the properties
>> linearly.
> It doesn't scan linearly.  It calls next-single-property-change, which
> traverses the interval tree we use for storing text properties.
> Please take a look at the implementation of
> next-single-property-change in textprop.c.
It finds the starting point by traversing the tree (presumably an 
O(log(number_of_properties)) operation (that's good), but then scans 
linearly. My example buffer has a different property for (almost) all 
characters, this means that this function will search the tree for ~500 
elements to conclude that there is no such property around. And it does 
this operation for every single character visible in the window, or 
something like this.

>> And it scans it for 500 characters. This file has a lot of
>> properties, this means a lot of unnecessary and duplicated work (because
>> it does this for each character displayed, or something like this). If
>> the composition property had its own list, then this problem wouldn't exist.
> If the composition property had its own data structure, Emacs would
> need to search them both when it looks for a change in _any_ property
> (something that happens quite a lot in other places), and handle
> various combinations of hits in both data structures.  That'd be a
> significant complication for a small gain.
If that's the case, then emacs could have a data structure which 
contains all the properties (like now), and one which points only to the 
composition properties. Yes, this is a complication, it will make emacs 
generally a little bit slower, but it would avoid freezes.

But maybe this is not the correct solution. I have a feeling that this 
500-character search area is not a good solution for the problem it 
tries to solve. Maybe it's a result of some optimization that was 
relevant a long time ago, but now it isn't, and it is actually a 
de-optimization if the buffer contains a lot of properties.

A lot of people wouldn't care if emacs used a little more CPU power in 
general (as an example, during general editing, it would use 30% CPU 
instead of 20%), if this means that there is no stuttering. That's why 
people (me included) set the garbage collector threshold to a high 
value, because of the introduced micro-stuttering by the GC. But I'm not 
even sure that having more specialized search structures would actually 
slow down emacs in general. Maybe the net result would be that not only 
emacs won't freeze, but it would also become generally faster.





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

end of thread, other threads:[~2023-03-26  7:14 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-21 20:01 bug#62352: Very slow scroll-down-line with a lot of text properties Herman, Geza
2023-03-21 20:26 ` Eli Zaretskii
2023-03-21 20:39   ` Herman, Géza
2023-03-21 21:58   ` Gregory Heytings
2023-03-25 11:58     ` Eli Zaretskii
2023-03-25 12:33       ` Herman, Géza
2023-03-25 12:42         ` Eli Zaretskii
2023-03-25 13:41           ` Herman, Géza
2023-03-25 14:02             ` Eli Zaretskii
2023-03-25 15:24               ` Herman, Géza
2023-03-25 16:20                 ` Eli Zaretskii
2023-03-25 17:38                   ` Herman, Géza
2023-03-25 17:49                     ` Eli Zaretskii
2023-03-25 21:39                       ` Herman, Géza
2023-03-26  4:55                         ` Eli Zaretskii
2023-03-26  7:14                           ` Herman, Géza

Code repositories for project(s) associated with this external index

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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.