Hacker News new | ask | show | jobs
by the-lazy-guy 1278 days ago
As an emacs user I have to say that IntelliJ (and pretty much any modern IDE) feels snappier than emacs configured to do anything comparable (lsp-mode/eglot, corfu/company, tree-sitter etc).

While IDEs may require more resources, they usually utilize them better.

Emacs is fundamentally single-threaded and not great at async things. With most of the logic written in a very slow non-JIT friendly language with naive blocking GC. Rendering pipeline is also a mess. Underlying internal datastructures are also very naive (which is a good thing when you implement things in C).

Things are slowly changing. emacs now is a lot better than 5 years. So may be in 10 years it will really rival more modern competitors in terms of performance.

Having said all this, emacs universality and extensibility keep me with it.

7 comments

The asyncness of Intellij IDEs annoys me at times. I'm used (from emacs) to bashing out a series of key combinations in the expectation that the effects of one will be complete before the next is processed. There are cases in Intellij IDEs (can't remember specifics, I'm blissfully back in emacsland and don't anticipate using an IDE again in the near future) where that can result in the later keypresses being processed before the window pops up or whatever from the earlier ones.

Just a minor annoyance, really. It would be nice for emacs to improve on the async front, but synchronicity does have its benefits when interacting with humans. I sometimes find the lack of asynchronous behaviour annoying in emacs, but not very often despite using EXWM (so locking up emacs means locking up the window manager).

This is a major annoyance in Visual Studio. You have to stay all the time waiting for feedback from the software, otherwise your commands will get processed on the wrong order.

And yes, autocompletion fires from many different keys, and if it misses the keys order, it will automplete into some completely different code.

I use IdeaVim on a Mac and I'm 80% sure it reorders keystrokes sometimes when I go between normal and insert mode.
I've never had this happen to me. Could you describe what happens a bit more clearly? Now I'm curious.
E.g. I'm in insert mode, then I press esc to go to normal mode and press j to go down one line. Instead, j is inserted.

I can't be sure I didn't just make a typo, but this has happened much more often in Intellij than in vim proper, so I think there is something going on where IDEA has a hiccup and scrambles or eats some inputs.

You can lower the autocomplete time threshold.
Yeah, Emacs's performance isn't really that great when going head-to-head as an IDE. Yes, it uses less memory and disk cache, but if we're talking about, e.g., working on a large Rust project with Emacs+lsp-mode/eglot+rust-analyzer, you're going to be in the ballpark of IntelliJ for memory usage, and the responsiveness will be nowhere near it. I will say, though, that the disk usage will be much less, and that has mattered for me on this 256GB disk MBP- between Xcode, Docker, and all these package managers for various languages, my disk space has become a precious commodity.

I still like Emacs. I prefer free software. I love Magit. But, the UI responsiveness is honestly frustrating when using LSP.

Wouldn't most of the slowness and memory use be due to rust-analyzer itself, which is notoriously slow and heavy for large projects? That has little to do with your IDE choice. Emacs devs are also working on tree-sitter parsing support, which might help reduce reliance on LSP more generally.
Emacs was built as a TUI, and it is built like a giant REPL; read input, evaluate command, print the output, and loop back. IDEs based on "modern" GUI frameworks (IntelliJ uses Swing, not exactly "modern") are inherently multi-threaded; you have the UI (or "main") thread that accepts events and updates the display, and ideally never blocks, while evaluation is performed on worker threads that can be canceled or scheduled as needed.

In practice, you can get far with Emacs' process supervision to avoid blocking the command loop, but some things that are trivial to offload to a worker thread are next-to-impossible to avoid blocking in Emacs. One of those things is parsing megabytes of JSON from an LSP server into Lisp data structures, which is baked into the Emacs core and will block redisplay.

Yes and no. I'm using rust-analyzer as an example, but I also use other language servers and observe similar UI lags/stutters.

When it comes to memory, my point is precisely that we should count the language server in the total and not just the Emacs process. So, when some other IDE is using multiple GB of memory on my Rust project, we have to compare that to my Emacs + rust-analyzer memory usage because both of those processes are providing the features that other-IDE is providing.

However, Emacs is also still kind of laggy when using LSP, even though the actual LSP server lives in another process and communicates mostly asynchronously. My understanding is that there are still bottlenecks in Emacs that make this not truly async. But, even if my LSP server of choice is extremely slow, why should that affect my cursor movement or typing speed in my Emacs buffer? I can see why it would be technically difficult to make it NOT introduce some lag, but it shouldn't in principle.

I haven't used Visual Studio Code, but my understanding is that it is plenty responsive to typing and cursor movement, even while using rust-analyzer.

For me Emacs pretty much grinds to a halt if I turn on showing line numbers.
Try to determine which one you're using by running "M-x linum-mode" and then "M-x display-line-numbers-mode". Note that you can use both at the same time (which would make zero sense: it's only to troubleshoot which one is problematic for you).

Do not use linum-mode anymore.

I can display buffers with hundreds of thousands of lines while showing the line numbers (Emacs 29 / native compilation / display-line-numbers-mode / CPU is an AMD 3700X with 32 GB of RAM) and Emacs is ultra responsive.

This is the kind of thing which infuriates me the most about emacs. There are thousands of similar but not equivalent ways of doing the same thing, both StackOverflow and the emacs wiki contains some (but not all, and not the same, and usually not the right, and usually contradictory) ways of doing things. And it's impossible to figure out which way is the right one without asking around in IRC channels.

Why oh why couldn't they improve linenum-mode instead of introducing a new mode.

> There are thousands of similar but not equivalent ways of doing the same thing, both StackOverflow and the emacs wiki contains some

What about the manual?

https://www.gnu.org/software/emacs/manual/html_node/emacs/Di...

The issues you described are mostly totally solved by looking in the manual first rather than externally.

> Why oh why couldn't they improve linenum-mode instead of introducing a new mode.

The double-edged sword of being very backwards compatible.

You’re probably using the old notoriously slow linum-mode. The newer line-number-mode is much snappier.
Since you mentioned the single-threadedness (and it’s relevant to the discussion of IDE/Emacs): what’s the status of any initiative to move Emacs to multiple cores? I’m single, and I hope to one day have a family etc.. If I end up staying single for long enough, helping move Emacs onto multiple cores is something I fantasize about contributing back to the community that has helped my life so much.

Does anyone have any input?

Emacs has support for multithreading (threads landed in 26), there was an article about the multithreaded model earlier this year (https://news.ycombinator.com/item?id=31559818), and the Elisp manual has a section on concurrency (section "Threads").

If you want to contribute to the future (pun intended) of multithreaded Emacs, your best bet may be to make a library that adds the nice concurrency facilities that will help library authors move to a multithreaded model. An actor model, green threads, or supervision trees may be avenues worth pursuing.

A big blocker is probably the dynamic binding, but trying to "fix" that would break a lot, if not all, of the existing libraries.

Please DON'T use the threads. As of 2018 it has many data races (https://nullprogram.com/blog/2018/05/31/), and resultant bugs will be a nightmare to track down.

The Emacs community has always got by through finding other ways to ensure responsiveness. It's true that each individual user shouldn't have to spend that much time on configuration (Doom Emacs and such distros help here), but still: people who find it slow must have misconfigured something, and on profiling, they'll find at most 1 or 2 features of their config that's eating all the CPU cycles. These won't actually be hard to fix, for a programmer, and threads wouldn't have helped matters, and at best only have hid the CPU-eater.

Yes, that post was mentioned in the article I linked:

Even Emacs current threads library suffers from data races which is one of the reason I believe it has not seen much adoption.

(where suffers from data races is a link to Chris Wellons' post).

The idea now is, as Chris states:

There really needs to be a safe, high-level API with clean thread isolation. Perhaps this higher-level API will eventually build on top of the low-level threading API.

While you are right that for 99% of standard usage single-threaded code is entirely fine, not utilizing between half to 15/16ths of potential processing power is a bit of a shame, especially considering it means doing something like indexing an entire project (yes, we can use GNU Global) will require the user to just sit and wait.

That "99% of standard usage" is the kicker, isn't it? The greybeards who opposed multithreading since the beginning tend to say that the remaining 1% of use cases is best done in an external process, ideally not even written in Emacs Lisp, so that Vimmers and the rest of the free software community can benefit. The GNU Global you mention embodies that ideal exactly.

I suppose if you still want that program to be written with Emacs Lisp, you could use async.el (https://github.com/jwiegley/emacs-async/) and there's finally an use-case for the threads: it'll be relatively safe to run those 16 threads only in the external Emacs-process.

Thanks for the pleasant surprise of encouraging feedback. I’m going to send this comment chain to my email inbox so that I can find it later. This is great.

It’s such a monumental task to hack on the basics of the text editing. I listened to a tts book; I think it was titled “The Art of the Text Editor” that delved a bit into some of the original philosophy of the text rendering etc. in Emacs. You might enjoy it If you ever need a boring book to read.

You probably mean The Craft of Text Editing -or- A Cookbook for an Emacs. I've skimmed it a bit before and it's on my reading list. First I'm still working my way through CRLS's Introduction to Algorithms and Aspnes' Notes on Data Structures and Programming Techniques.
Links to the electronic versions of this out of print book: https://www.finseth.com/craft/
Warning, low self-esteem and anxiety ahead:

I really wish I had the capacity and capability to pursue this effort. I barely have the energy to be good at shitty DevOps and Terraform, writing basic Python and Node is so time consuming. I can do some Lisp, but only at the basic level of init.el and adapting a few configuration examples.

I do the good thing and donate to the FSF monthly but I wish I could donate directly to Emacs because I use it every day. Even better would be to sponsor the development of a concurrency library and port of major things like magit or org.

> I wish I could donate directly to Emacs

I've requested this whenever I've had the chance. I would be very happy to donate some cash to get core Emacs improved. Emacs has made a huge positive difference to my professional life for years. Unfortunately this is not made easy.

I think one of the issues last time I looked into it was that FSF couldn't have donations on a platform that was potentially tarnished by having e.g. non-free javascript running on it somewhere... All very correct and in tune with their mission I'm sure, and part of the reason why Emacs etc. has been so successful, but it would be nice if they were a bit more pragmatic occasionally.

Most of what Emacs does just won’t benefit from massive parallelism. CPUs are ridiculously fast at processing text (AVX2 instructions can process vectors of 32 bytes at a time), and most of the stuff that _can_ benefit from parallelism should just be moved to an external process. For example, querying your compiler for a list of methods that apply to the current object, or a list of functions that start with “Foo” are mostly moving to external processes using LSP as the communication protocol. It would be nice for some things to use background threads, like Gnus polling your IMAP server for email, and Emacs does have the threading mechanisms to handle that now. It’s just a matter of time before someone does the work :)
I disagree. When I am running a compilation (with output being dumped into a visible buffer) + query magit for large commit. Over tramp. Things noticeably freeze. Technically it all is async. Practically, it is implemented as polling things on main thread with some witing happening in non-async fashion.

> For example, querying your compiler for a list of methods that apply to the current object, or a list of functions that start with “Foo” are mostly moving to external processes using LSP as the communication protocol.

That's why we have lsp-bridge and lsp-mode emacs fork :) Both of which build some infrastructure to avoid doing communication work with lsp-mode work in main emacs thread. So, heavy emacs users are building some async machinery which wraps another already async and relatively lightweight protocol, because core emacs facilities can't keep up with it. Architecturally it is kind of insane.

I think, lsp-mode fork is doing the right thing (from practical POV; it goes against "emacs is just an elisp interpreter" ideology though) and hope it gets into core at some point. A better solution would have being having first class async and background threads support at the elisp level. Which would never happen due to elisp messiness.

https://github.com/emacs-lsp/emacs https://github.com/manateelazycat/lsp-bridge

Hey! Exactly the response I was looking for. Thanks for this.

Your response reminded me of an Intel project about regular expressions. I think it’s called hyperscan, but I’m not sure. It’s a regex engine (I think that’s the correct terminology) that makes dedicated use of very specific Intel instructions to reach incredible regex speeds on Intel processors. Would be cool if Emacs could make use of this tech when configured properly. I bet there are some internals that rely on regex.

Does emacs use specialized instruction sets like AVX2?
Alas, no. But the power is right there, all we have to is stretch forth our hands and use it.
Ha! No. Native compilation just snuck in a little while ago. SIMD will take... longer.
Emacs now has AoT compilation of EmacsLisp, using GCC's JIT library:

https://blog.phundrak.com/emacs-29-what-can-we-expect/

This is awesome! I use it since initial "gccemacs" announcements.

It is still order of magnitude slower than JS. Dynamic binding, ability to redefine or advice any function and fundamentally cache-unfriendly core datastructures (cons-cells) and absence of JIT hotspot optimization do not help to catch up with competition :(

For anything lsp related, this fork by the lsp authors has been noticeably snappier and more pleasant to use for me (large rust project): https://old.reddit.com/r/emacs/comments/ymrkyn/async_nonbloc...

It moves the json rpc stuff to be async, and makes a real difference in terms of perceived performance.

Doesn't lsp run in a separate process for other editors as well? I've only found emacs slow for larger Java projects in the past, using eclim, and font-locking very large javascript files. But the new lsp-java seems to have cleared that up, and I'm not sure what changed w/ javascript-mode, but it also seems much faster since a few years ago.
LSP runs in a separate process, but handling that process is synchronous in Emacs, I think. So if LSP is slow for something, Emacs will block.
Yes it gives us both a super-predictable queued operations model for using emacs, and serial operation in a parallel world. Years ago when single core performance was king this wasn't an issue, but now we're stuck having to optimize the long hot path.
The next AMD processor should have an optional Emacs dedicated core.
This.

I tried to switch to Emacs multiple times, but the lagginess in a non-trivial setup just kills me.

I always went back to Neovim.

I tried to switch to emacs but couldn't find an introductory tutorial that was written for mere mortals.

Currently use vim, or things like gedit and kate that come w/ a DE. I would really like to learn to use emacs, but the last so called "beginner" tutorial I tried started with _learn a new lisp dialect so you can write emacs extensions_. Seriously.

Does anyone know where to find a tutorial that teaches you how to do things like open a file, type some stuff, and save the file?

Maybe show whitespace change highlighting modes and a few hotkeys if I want to get fancy... but like otherwise normal beginner stuff?

Aside from the built in docs, mastering emacs is quite beginner friendly. It's aimed at people with prior programming experience, but not any emacs experience. You can get a few chapters for free.
Maybe https://emacs.zeef.com might be your friend? There are some tutorials listed there for Emacs.
There's a built-in tutorial, called by pressing ctrl-h (or f1) and then t.