1

I'm experiencing serious performance degradation whenever I'm in dart-mode and LSP is enabled. This is a strange one because Emacs I don't experience any performance degradation on a fresh instance but within half-hour or so of editing Dart source files, the utilization of one of the CPU cores goes up to about 100% whenever the active mode's major mode is dart-mode and LSP is enabled.

Had to resort to asking the community for help because I ran Emacs' profiler (profiler-start -> stop -> report) multiple times and I always get the following unhelpful results:

    1434  48% - timer-event-handler
    1213  41%  - timer-activate
    1181  40%   - timer--activate
     309  10%      timer--time-less-p
     106   3%    timer-inc-time
      15   0%  - apply
      15   0%   - auto-revert-buffers
       8   0%      auto-revert--buffer-candidates
    1193  40% - redisplay_internal (C function)
     858  29%  - eval
     536  18%     mapconcat
     178   6%   - if
       4   0%      display-graphic-p
      34   1%     lsp--progress-status
      20   0%   - dap-mode-line
      20   0%    - dap--cur-session
       4   0%       lsp-workspace-get-metadata
      12   0%   - unless
       4   0%      if
       4   0%     mode-line-eol-desc
      55   1%  - #<compiled -0x13416d349593be12>
      55   1%   - apply
      47   1%    - redisplay--pre-redisplay-functions
      31   1%     - run-hook-with-args
      31   1%      - redisplay--update-region-highlight
      23   0%       - #<compiled 0x7c2ddd508885108>
      23   0%        - apply
      12   0%         - rectangle--unhighlight-for-redisplay
       4   0%            #<compiled 0x1aa0daf71b430848>
      52   1%  - mode-line-default-help-echo
      40   1%   - window-at-side-p
      32   1%      window-pixel-edges
     305  10% - ...
     305  10%    Automatic GC
       4   0% - command-execute
       4   0%  - funcall-interactively
       4   0%   - eval-last-sexp
       4   0%      elisp--eval-last-sexp

As you can see above, a large chunk of the execution is spent inside timer-related calls but it's not possible to identify the functions that are triggering the timers. Some time is also spent in internal Emacs functions responsible for rendering but it's not clear what's causing the redisplays. My question for the community is how can I dig deeper and improve the backtraces above? More specifically:

  1. How do I identify the functions that are repeatedly triggering the timers, ideally that doesn't involve altering/redefining the timer functions?
  2. How do I identify the causes for the redisplays?

(lsp-doctor) gives:

Checking for Native JSON support: OK
Check emacs supports `read-process-output-max': OK
Check `read-process-output-max' default has been changed from 4k: OK
Byte compiled against Native JSON (recompile lsp-mode if failing when Native JSON available): OK
`gc-cons-threshold' increased?: OK
Using `plist' for deserialized objects? (refer to https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization): OPTIONAL
Using emacs 28+ with native compilation?: OK

Running Emacs v29.0.50 (latest from master as of now).

NickD
  • 27,023
  • 3
  • 23
  • 42
migdsb
  • 113
  • 5
  • 1
    `M-x list-timers` will show you active and idle timers, how often/when they trigger, and what function they run. – NickD Mar 13 '22 at 16:37
  • 1
    @NickD thank you so much -- `list-timers` set me on the right track and I can now see I had several dozens of timers on repeat every .2s running the same function. Clearly a bug in the `lsp-dart` package. Thanks again. If you submit a post, I will accept it as the answer to my question. Strange that `list-timers` isn't an enabled command though. – migdsb Mar 13 '22 at 17:11

1 Answers1

3

M-x list-timers will show you active and idle timers, how often/when they trigger, and what function they run. The command is disabled by default, but you can enable it for one-time use or permanently (or you can always execute from lisp with no questions asked; e.g. go to your *scratch* buffer and type (list-timers) followed by C-j).

As the OP found out, there were dozens of duplicate timers running. In the buffer where list-timers put its output, you can cancel a timer by pressing c (timer-list-cancel) on that timer's line. You can move to the next/previous timer line with n/p. For other possible actions in the buffer, see its mode map with C-h m.

NickD
  • 27,023
  • 3
  • 23
  • 42