On 12/05/2020 11.27, Eli Zaretskii wrote: > (The code is actually in a subroutine called by internal-lisp-face-p.) > Which means face-set-after-frame-default, which loops over all of the > faces, runs with O(n²) complexity in the number of faces. > > So I think if we want to support such large amounts of faces, we > should not store them in alists, but in a more efficient data > structure. Indeed, you're completely right; thanks! Replacing face_alist and Vface_new_frame_defaults with hash tables makes the worst example about 10 times faster, and with that change tooltips now take 30 to 50ms to display instead of 500-600ms in my real-life use case (my usual config). I have attached a patch. I left a few questions in the code; I hope that's OK. I have a few more questions that are not part of the code: * I removed the function frame-face-alist and changed the type of the variable face-new-frame-defaults. Both were documented as internal. Should I add an ELisp implementation of frame-face-alist for compatibility? (It wouldn't be a perfect shim, since modifying its return value wouldn't do the same). For face-new-frame-defaults it's a bit trickier, since the variable now holds a hash table. Should I change its name to make the change obvious, at least? * The name face_hash isn't ideal, since there's already a distinct notion of face hashes (hash codes). Can you think of a better name? * I imagine that this change needs to be advertised somewhere, but I'm not sure where; NEWS? Lastly, do the following new profiles suggest other opportunities for improvement? - ... 499 52% Automatic GC 499 52% - command-execute 454 47% - call-interactively 454 47% - funcall-interactively 433 45% - eval-defun 427 44% - elisp--eval-defun 427 44% - eval-region 426 44% - my-bench-x-tip 426 44% - let 406 42% - list 406 42% - let 406 42% - x-show-tip 390 40% - face-set-after-frame-default 387 40% - face-spec-recalc 374 39% - make-face-x-resource-internal 296 30% - set-face-attributes-from-resources 273 28% - set-face-attribute-from-resource 219 22% + face-name 65 6% + face-spec-reset-face 62 6% + face-spec-set-2 6 0% + face-spec-choose 2 0% + face-list 2 0% + frame-windows-min-size 1 0% + my-def-many-faces 20 2% + end-of-defun 1 0% + execute-extended-command 6 0% + byte-code 21 2% + redisplay_internal (C function) 2 0% tooltip-hide 1 0% - command-execute 768 80% - call-interactively 768 80% - apply 768 80% - call-interactively@ido-cr+-record-current-command 768 80% - apply 768 80% - # 768 80% - funcall-interactively 768 80% - eval-defun 715 74% - apply 713 74% - # 712 74% - elisp--eval-defun 712 74% - eval-region 709 74% - apply 709 74% - # 709 74% - endless/eval-overlay 709 74% - apply 709 74% - # 707 74% - my-bench-x-tip 707 74% - let 689 72% - list 689 72% - let 689 72% - x-show-tip 676 70% - face-set-after-frame-default 674 70% - face-spec-recalc 660 69% - face-spec-set-2 350 36% - apply 348 36% - set-face-attribute 342 35% - internal-set-lisp-face-attribute 342 35% - frame-set-background-mode 331 34% - face-spec-recalc 284 29% - make-face-x-resource-internal 235 24% - set-face-attributes-from-resources 216 22% - set-face-attribute-from-resource 174 18% - face-name 36 3% + check-face 21 2% + face-spec-reset-face 40 4% + face-spec-set-2 4 0% + face-attr-match-p 24 2% face-spec-choose 1 0% + face-list 1 0% - make-face-x-resource-internal 248 25% - set-face-attributes-from-resources 215 22% - set-face-attribute-from-resource 169 17% + face-name 36 3% + face-spec-reset-face 54 5% + face-spec-choose 2 0% + face-list 1 0% + my-def-many-faces 18 1% + beginning-of-defun 1 0% end-of-defun 1 0% + # 2 0% + smex 53 5% - ... 182 19% Automatic GC 182 19% + redisplay_internal (C function) 3 0% Also, since the GC seems to be a significant part, here's a memory profile: - command-execute 305,314,261 99% - call-interactively 305,314,261 99% - funcall-interactively 305,262,257 99% - eval-defun 303,318,241 98% - elisp--eval-defun 303,317,185 98% - eval-region 303,296,332 98% - my-bench-x-tip 303,295,276 98% - let 273,049,377 89% - list 273,049,377 89% - let 273,049,377 89% - x-show-tip 177,538,262 57% - face-set-after-frame-default 175,519,190 57% - face-spec-recalc 174,935,046 57% - make-face-x-resource-internal 138,435,960 45% + set-face-attributes-from-resources 138,407,728 45% + face-spec-reset-face 36,126,838 11% + face-spec-choose 74,360 0% + face-spec-set-2 21,216 0% + face-list 554,400 0% + frame-windows-min-size 7,676 0% + run-at-time 4,352 0% setq 4,224 0% + float-time 3,888 0% + my-def-many-faces 30,245,899 9% + internal-macroexpand-for-load 1,056 0% + end-of-defun 4,160 0% + beginning-of-defun 2,112 0% + execute-extended-command 1,944,016 0% + byte-code 52,004 0% + redisplay_internal (C function) 1,427,117 0% > No Emacs version information? Woops. Sorry! GNU Emacs 28.0.50 (build 10, x86_64-pc-linux-gnu, GTK+ Version 3.22.30, cairo version 1.15.10) of 2020-05-10 Thanks again for the pointers, Clément.