* 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 public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).