Eli Zaretskii wrote: > > > Date: Thu, 10 Apr 2025 00:33:03 +0900 > > From: kobarity > > Cc: JD Smith , > > 77620@debbugs.gnu.org > > > > Eli Zaretskii wrote: > > > > > > > In #77588 this was shown to lead to long intermittent pauses with eglot in large python buffers, but the issue is more general for any packages using thingatpt in python buffers. > > > > > > The Lisp profile, which seems to point to the likely culprit, is > > > below. > > > > > > Maybe kobarity (CC'ed) will have some ideas or suggestions. > > > > > > 345 89% - ... > > > 343 89% - cond > > > 343 89% - let > > > 343 89% - condition-case > > > 343 89% - progn > > > 343 89% - save-excursion > > > 173 44% - funcall > > > 173 44% - thing-at-point--end-of-sexp > > > 173 44% - let > > > 173 44% - if > > > 173 44% - condition-case > > > 173 44% - forward-sexp > > > 173 44% - python-nav-forward-sexp > > > 173 44% - python-nav--forward-sexp > > > 173 44% - python-info-statement-ends-block-p > > > 173 44% - python-nav-end-of-block > > > 96 24% - python-nav-end-of-statement > > > 69 17% - syntax-ppss > > > 67 17% parse-partial-sexp > > > 27 7% - python-info-line-ends-backslash-p > > > 27 7% - syntax-ppss > > > 27 7% parse-partial-sexp > > > 76 19% - python-nav-beginning-of-block > > > 76 19% - python-nav-backward-block > > > 76 19% - python-nav-forward-block > > > 75 19% - python-syntax-context-type > > > 75 19% - syntax-ppss > > > 74 19% parse-partial-sexp > > > 1 0% - python-nav-beginning-of-statement > > > 1 0% point-marker > > > 1 0% current-indentation > > > 170 44% - let > > > 170 44% - if > > > 170 44% - funcall > > > 170 44% - thing-at-point--beginning-of-sexp > > > 170 44% - let > > > 170 44% - if > > > 170 44% - forward-sexp > > > 170 44% - python-nav-forward-sexp > > > 170 44% - python-nav--forward-sexp > > > 170 44% - python-info-statement-ends-block-p > > > 170 44% - python-nav-end-of-block > > > 95 24% - python-nav-end-of-statement > > > 63 16% - syntax-ppss > > > 62 16% parse-partial-sexp > > > 31 8% - python-info-line-ends-backslash-p > > > 31 8% - syntax-ppss > > > 30 7% parse-partial-sexp > > > 1 0% re-search-forward > > > 75 19% - python-nav-beginning-of-block > > > 75 19% - python-nav-backward-block > > > 75 19% - python-nav-forward-block > > > 72 18% - python-syntax-context-type > > > 72 18% - syntax-ppss > > > 71 18% parse-partial-sexp > > > 2 0% re-search-backward > > > 1 0% - python-nav-beginning-of-statement > > > 1 0% - python-info-line-ends-backslash-p > > > 1 0% - syntax-ppss > > > 1 0% syntax-ppss--data > > > 2 0% - python-shell-get-process > > > 2 0% - python-shell-get-buffer > > > 2 0% - seq-some > > > 2 0% + seq-do > > > 32 8% Automatic GC > > > 6 1% - command-execute > > > 6 1% - call-interactively > > > 6 1% - byte-code > > > 6 1% - read-extended-command > > > 6 1% - read-extended-command-1 > > > 6 1% - completing-read > > > 6 1% - completing-read-default > > > 6 1% - read-from-minibuffer > > > 1 0% redisplay_internal (C function) > > > 2 0% redisplay_internal (C function) > > > > Please give me some time as I'm trying to improve performance. > > Sure, there's no rush. TIA. In the process of looking into #77620, I noticed bugs in the end-of-block detection functions. So I submitted a patch to #77941. Please note that the patch attached to this mail assumes the #77941 patch. In my environment, the next test shows about 2 seconds on _axes.py's L8817. > (/ (car (benchmark-run 10 (thing-at-point 'sexp))) 10) But there are worse cases. Let's add the next two lines to the end of _axes.py with 4-character indent. a = 1 b = 2 The same measurement at the end of line "a = 1" gives 6 seconds; at the end of line "b = 2" it gives 7 seconds. This is because it calls time-consuming functions many times. `python-nav-beginning-of-block' and `python-nav-end-of-block' are very time consuming if the block is large. In the case of _axes.py, "class Axes" is a large block with over 8000 lines. However, `python-nav-forward-sexp' that (thing-at-point 'sexp) calls does not necessarily have to perform these functions. They are just called by `python-info-statement-ends-block-p': (Before #77941 patch) (defun python-info-statement-ends-block-p () "Return non-nil if point is at end of block." (let ((end-of-block-pos (save-excursion (python-nav-end-of-block))) (end-of-statement-pos (save-excursion (python-nav-end-of-statement)))) (and end-of-block-pos end-of-statement-pos (= end-of-block-pos end-of-statement-pos)))) The logic is that if the end of the current statement is the end of the current block, then it is the last line of the block. This is not wrong, but the problem is that it takes time to move to the end of the block. Fortunately, in many cases it is much easier to determine that it is not the end of the block. Specifically, if the indentation of the next statement is equal to or greater than the indentation of the current statement, then the current statement cannot be the end of the block. The attached patch 0001-Performance-optimization-of-python-info-statement-en.patch takes advantage of this to improve the performance of `python-info-statement-ends-block-p'. Please remember that it assumes the #77941 patch is applied. 0001-Performance-optimization-of-python-info-statement-en.patch solves the performance problem with _axes.py, but does not have much effect on the problem with lines "a = 1" and "b = 2" added above. The attached patch 0002-Add-cache-to-Python-block-navigation-functions.patch is an attempt to introduce a cache to alleviate this problem. I measured the following to disable the cache for each benchmark. (/ (car (benchmark-run 10 (progn (thing-at-point 'sexp) (setq python-nav-cache nil)))) 10) It shows about 1.6 seconds for both "a = 1" and "b = 2" lines. I believe 0002-Add-cache-to-Python-block-navigation-functions.patch is a PoC level patch, so I welcome opinions on what you think of this direction.