I recently changed jobs and found myself in a position where I would need to do a lot of work on remote machines. Since I am Emacs user, the most common way to do this is using TRAMP (Transparent Remote access, Multiple Protcol). TRAMP is an Emacs package that let’s you treat a remote host like a local system, similar to VSCode Remote Development Extension. I had used TRAMP before and it tended to be slow. Since I would be using it all day now I figured I should take some time to make it faster.
TRAMP really is an amazing piece of technology. It supports a huge number of protocols and it lets you pretend that you are working on a local system. You can copy files around, run programs, run shells, and for the most part everything just works.
But TRAMP unfortunately has a propensity for being slow. Sometimes this is not TRAMPs fault and comes from how it is used. In my testing, each call through TRAMP takes about 50-100ms. Compare that to a normal external process call in Emacs which would take around 1 ms. Workflows that work fine on a local machine become unbearable when working remotely. However, this doesn’t mean that TRAMP has to be slow. If you run emacs -Q and use TRAMP you will normally find it quite snappy.
First, let’s set up some of the basic settings. These settings below will prevent TRAMP from creating a bunch of extra files and use scp directly when moving files.
( setq remote-file-name-inhibit-locks t tramp-use-scp-direct-remote-copying t remote-file-name-inhibit-auto-save-visited t )
In order to open a file on a remote host, TRAMP needs to copy the file contents to your local machine. Most TRAMP methods have two ways to copy files; inline or out-of-band. Out-of-band will use external methods like rsync or scp , while inline will send compressed base64 encoded text over the SSH session and then decode it on the other side. This works best for small files where the overhead of creating a new connection is not worth it. This is by default set to 10KB. However, I found in my testing that the best value was much bigger than that.
You can see in the graph above that inline is faster all the way up until about 2MB. After that point inline continues to grow linearly (the x-axis is logarithmic) while the out-of-band copying is sub-linear. Generally the slower the connection, the bigger the gap between inline and out-of-band. In the graph above it is 250ms, but on some slower connections I have observed that gap to be closer to 750ms. Interestingly, the cutoff always seems to be around 2MB, at least on my machines.
( setq tramp-copy-size-limit ( * 1024 1024 ) ;; 1MB tramp-verbose 2 )
I also found that using rsync as your method makes updating an existing file (i.e. making a small change to a file and saving it) about 3-4 times faster than using scp . I don’t use rsync though because it breaks remote shells.
When creating a new process in Emacs, you have two options: synchronous or asynchronous. Async processes have historically been really slow over TRAMP, because it has to create a new connection for every async process. However recent version of TRAMP have added a feature called direct async process that makes this significantly faster. This feature alone will take many packages (like magit or git-gutter) from completely unusable to bearable over TRAMP. Here is how you configure it with TRAMP 2.7 .
... continue reading