-
Notifications
You must be signed in to change notification settings - Fork 0
Description
I found this project on reddit and I am excited that you are working on it. I have recently been working from a position where I use tramp all day for work and hence run into the performance issue with it first hand. As I understand it, you are using libssh to create an Emacs module to use as a tramp handler.
What actually makes this faster? As I understand it, you still need to send shell commands to the remote host and read the replies. I can see that setting up the commands and parsing the output is faster in Rust than lisp. Is that essentially what is making the difference? Do you have a sense of how much faster it is?
I have been doing some profiling of tramp to to understand where time is being spent. Here is a couple of my findings:
Case 1: Copying 27 files from one directory on the remote host to another directory using dired
90% of the time is spent in tramp-send-command waiting for output.
We end up with 350 commands being sent across SSH.
88 file-directory-p
55 file-exists-p (one for source and dest + directory)
55 file-truename (one for source and dest + directory)
27 copy
27 file-readable-p
27 calls to ls. I think this is for file-modes?
57 file-attributes (one for source and dest + directory)
27 calls to cd (I think this is for the call to ls)
1 call to tramp_perl_file_name_all_completions
Note that each command is sent serially. Meaning that it can't start the next command until it get the result back from the current one. Even if tramp-libssh was able to have multiple threads with new connections, it wouldn't be able to use them because we are getting the commands one at a time from elisp, which is single threaded.
Case 2: Opening a file over tramp
Believe it or not, actually opening the file is not the slow part. There are a bunch of auxilary commands that come with it.
41 calls to tramp-send-command
16 file-directory-p
6 file-exists-p
2 file-readable-p
5 calls to git for `magit-toplevel`
4 file-attributes
1 file-readable-p
1 file-symlink-p
1 call to open the file
1 call to `tramp_bundle_read_file_names` to check for `.git` in each parent directory
1 call to `tramp_perl_file_name_all_completions`there are six calls to check for set_system_mode exists in both the current directory and the root. This is part of ffap-guess-file-name-at-point in file-name-at-point-functions.
It has to check the directories of each level of hierarchy going all the way to the top. This is for locate-dominating-file as part of vc-find-root.
Checks for git directories going all the way to the top as well. This is part of forge-bug-reference-setup in find-file-hook calling vc-registered. But there is a tramp specific backend for vc-registered that handles it with a "bundled" command that checks them all at once.
How can we reduce the number of calls?
Making the calls the faster is certainly an improvement, but I wonder if we can't reduce the number of total calls. My approach is to have a client-side and sever-side helper programs. The server-side one is essentially a file watcher. it looks at directories that tramp has interacted with in the past and looks for any changes (modified files, new files, deleted files, changes in permission etc). If it find any it sends them to the client side program. When tramp is going to send a command, it will send it to the client-side program (which could just be a Emacs dynamic module). If it has the data cached, it just returns the value. If not, it sends a command to the server-side to get the data. This would essentially make tramp as fast as working only client side once you have opened a directory.
Curious your thoughts.