emacs-csharp / csharp-mode

A major-mode for editing C# in emacs
GNU General Public License v3.0
155 stars 47 forks source link

Long strings slow down Emacs considerably #138

Closed martinjlowm closed 4 years ago

martinjlowm commented 5 years ago

Something as simple as

string abc = "defdefdefdefdef...";

makes Emacs unresponsive (probably indentation related) - not even C-g cancels whatever is going on. The amount of time Emacs hangs increases with the length of abc.

This happens on Emacs 26.1 (OS X 10.12.6) with --no-init-file and csharp-mode (csharp-mode-20181011.718) enabled.

josteink commented 5 years ago

Emacs (unfortunately) has a long history of terrible performance with long lines.

You may be able to replicate the same behaviour in a fundamental-mode buffer, but I honestly won’t be surprised if it’s worse with csharp-mode active.

How long lines are we talking about here?

martinjlowm commented 5 years ago

I noticed it from a base64-encoded (non-wrapped) string of just under 10k characters.

josteink commented 5 years ago

Yeah. That will probably kill any Emacs-mode, not just csharp-mode.

That has to be fixed in core Emacs.

martinjlowm commented 5 years ago

10k characters is basically nothing in this scenario. To compare, c-mode starts to slow down slightly at 100k (non-wrapping) lines.

josteink commented 5 years ago

C# had much more complex grammar than basic C has. Thus the (all elisp) parser is more involved too.

This has a cost, especially noticeable wrt speed of fortification, and particularly so for long lines.

You may consider “so long” a viable work-around for such cases:

https://www.emacswiki.org/emacs/SoLong

martinjlowm commented 5 years ago

SoLong is definitely a good tip. I didn't know of that package! Thank you :)

For reference, check out the following (30s split and sped up) GIFs comparing csharp-mode, c-mode and javascript-mode (in order) which all use cc-mode (if I'm not mistaken):

1/4 #1

2/4 #2

3/4 (too big for embedding) https://i.imgur.com/uEvD3Gf

4/4 #4

josteink commented 5 years ago

If you want to get involved and profiling and analyzing where csharp-mode is slowing down relative to the other modes, I’m all ears.

If that results in a PR I will be first in line to review!

Unfortunately right now I don’t have time or motivation to go with those initiatives myself.

Are you willing to take a look?

martinjlowm commented 5 years ago

I might. :)

If I find some time, I'll take a look at it.

martinjlowm commented 5 years ago

A quick memory profile reveals the culprit:

- redisplay_internal (C function)                       3,130,139,663  99%
 - jit-lock-function                                    3,117,177,995  98%
  - jit-lock-fontify-now                                3,117,172,715  98%
   - jit-lock--run-functions                            3,117,156,171  98%
    - run-hook-wrapped                                  3,117,156,171  98%
     - #<compiled 0x452818e1>                           3,117,156,171  98%
      - font-lock-fontify-region                        3,117,156,171  98%
       - c-font-lock-fontify-region                     3,117,135,051  98%
        - font-lock-default-fontify-region              3,117,135,051  98%
         - font-lock-fontify-keywords-region            3,116,713,974  98%
            c-font-lock-<>-arglists                     3,104,775,374  98% <--
          - c-font-lock-declarations                        5,346,092   0%
           + c-find-decl-spots                              5,341,868   0%
          + c-font-lock-cut-off-declarators                 1,432,360   0%
          + #<compiled 0x44abe6fd>                            634,538   0%
          + c-font-lock-complex-decl-prepare                  491,510   0%
            #<compiled 0x44c4f6f1>                            266,516   0%
          + #<lambda 0x8b497ab0f80>                           241,388   0%
            #<compiled 0x41771d45>                            241,378   0%
            #<compiled 0x44edd3b1>                            222,192   0%
            #<compiled 0x44abf481>                            214,788   0%
            #<compiled 0x44af1991>                            208,878   0%
            #<compiled 0x40fcdf29>                            208,364   0%
            #<compiled 0x44ab8229>                            198,754   0%
            #<compiled 0x41735be9>                            197,628   0%
            #<compiled 0x44abc6fd>                            190,428   0%
          + c-font-lock-enclosing-decls                       159,052   0%
         + font-lock-fontify-syntactically-region             421,077   0%
 + eval                                                    12,961,668   0%
- command-execute                                          30,893,785   0%
 - call-interactively                                      30,893,785   0%
  - funcall-interactively                                  30,893,785   0%
   + kill-ring-save                                        18,350,712   0%
   + counsel-M-x                                            7,388,634   0%
   + yank                                                   4,725,637   0%
   + self-insert-command                                      424,054   0%
   + left-word                                                  2,080   0%
   + set-mark-command                                             242   0%
  internal-echo-keystrokes-prefix                              49,632   0%
- flycheck-display-error-at-point-soon                         14,696   0%
 - flycheck-overlays-at                                        14,696   0%
  - flycheck-filter-overlays                                   14,536   0%
     seq-filter                                                14,536   0%
  eshell-output-filter                                         12,592   0%
+ timer-event-handler                                          11,404   0%
  ...                                                               0   0%

Removing c-recognize-<>-arglists and making csharp-mode base off of c-mode instead, things start to become snappy:

1 file changed, 3 insertions(+), 3 deletions(-)
csharp-mode.el | 6 +++---

modified   csharp-mode.el
@@ -449,7 +449,7 @@ Most other csharp functions are not instrumented.
   ;; mode as the fallback for the constants we don't change here.
   ;; This needs to be done also at compile time since the language
   ;; constants are evaluated then.
-  (c-add-language 'csharp-mode 'java-mode))
+  (c-add-language 'csharp-mode 'c-mode))

 ;; ==================================================================
 ;; end of c# upfront stuff
@@ -1184,8 +1184,8 @@ Currently handled:
 ;; C# does generics.  Setting this to t tells the parser to put
 ;; parenthesis syntax on angle braces that surround a comma-separated
 ;; list.
-(c-lang-defconst c-recognize-<>-arglists
-  csharp t)
+;; (c-lang-defconst c-recognize-<>-arglists
+;;   csharp t)

 (c-lang-defconst c-identifier-key

[back]

Obviously, this is not a solution and it affects java-mode as well - my search continues...

josteink commented 5 years ago

A quick memory profile reveals the culprit

Nice. Care to share how you did that? :)

Obviously, this is not a solution and it affects java-mode as well - my search continues

It's clearly not a solution, but just for fun, I decided to run make test:

Ran 29 tests, 28 results as expected, 1 unexpected (2019-05-16 11:21:05+0200, 0.905807 sec)

That's a whole lot better than I expected :laughing:

martinjlowm commented 5 years ago

M-x profiler-start, do your thing and then run M-x profiler-report. It was new to me as well :P

easbarba commented 4 years ago

@martinjlowm @josteink

Emacs 27.1 will arrive with this long-awaited fix:

https://github.com/emacs-mirror/emacs/commit/4ac905f88f10439ca0795b217a046e3a62895fc4

I don't have anymore this slowness in long JSON and minified JS files :D

more information: https://www.reddit.com/r/emacs/comments/ccoksw/solong_mitigating_slowness_due_to_extremely_long/

josteink commented 4 years ago

That's great news, @lxbarbosa !

Since the fix is in Emacs itself and not in csharp-mode, I'll close this issue for now. Hope that's OK with everyone.