From: Michael Albinus <michael.albinus@gmx.de>
To: Philipp Stephani <p.stephani2@gmail.com>
Cc: emacs-devel@gnu.org
Subject: Re: Remote asynchronous processes
Date: Wed, 06 May 2020 13:59:34 +0200 [thread overview]
Message-ID: <87a72ltgt5.fsf@gmx.de> (raw)
In-Reply-To: <87tv1nwuv4.fsf@gmx.de> (Michael Albinus's message of "Mon, 13 Apr 2020 12:19:43 +0200")
[-- Attachment #1: Type: text/plain, Size: 2622 bytes --]
Michael Albinus <michael.albinus@gmx.de> writes:
Hi,
> On the Tramp ML, there is a discussion about performance of remote
> asynchronous processes. start-file-process / make-process take too much
> time to finish.
>
> One of the reasons is, that Tramp opens first a shell on the remote
> host, performs sanity checks, and runs the command after that. Well, I
> cannot change this in general; the sanity checks have been added due to
> feedback from users.
>
> One idea to change the situation is, to remove all sanity checks from
> make-process. That is, if a user has a default directory
> "/ssh:user@host:/path/to/dir", and if he calls
>
> (make-process
> :name "test"
> :buffer (current-buffer)
> :command '("cmd")
> :file-handler t))
>
> this is translated directly into
>
> ssh -l user -o ControlMaster=auto -o ControlPath='tramp.%C' \
> -o ControlPersist=no host "cd /path/to/dir; cmd"
>
> This would improve performance significantly. The drawback is, that
> Tramp does not perform convenience checks, like password handling.
I have played with this idea, and the output is the appended file
tramp-make-process.el. It changes the make-process implementation of
Tramp for all methods defined in tramp-sh.el (like "ssh") and
tramp-adb.el.
This is a proof-of-concept, and shouldn't be used for production. Read
the commentary in the file for limitations.
However, the speed optimization is remarkable. I've tested it with the
following code snippet:
--8<---------------cut here---------------start------------->8---
(let ((tramp-verbose 0)
(default-directory "/ssh::/"))
;; Fill the caches.
(start-file-process "" nil "true")
;; Run benchmark.
(benchmark-run 10
(start-file-process "" nil "true")))
--8<---------------cut here---------------end--------------->8---
In the default case, the result is
--8<---------------cut here---------------start------------->8---
(3.623666842 80 1.183512312)
--8<---------------cut here---------------end--------------->8---
If tramp-make-process.el is loaded, the result is
--8<---------------cut here---------------start------------->8---
(0.022762177 0 0.0)
--8<---------------cut here---------------end--------------->8---
Similar results, if I use "/adb::/" as default directory:
--8<---------------cut here---------------start------------->8---
(4.599374061 2 0.03429497299999973)
--8<---------------cut here---------------end--------------->8---
vs
--8<---------------cut here---------------start------------->8---
(0.013003183 0 0.0)
--8<---------------cut here---------------end--------------->8---
Comments?
Best regards, Michael.
[-- Attachment #2: tramp-make-process.el --]
[-- Type: text/plain, Size: 7987 bytes --]
;;; tramp-make-process.el --- Tramp alternative make-process -*- lexical-binding:t -*-
;; Copyright (C) 2020 Free Software Foundation, Inc.
;; Author: Michael Albinus <michael.albinus@gmx.de>
;; Keywords: comm, processes
;; Package: tramp
;; This file is part of GNU Emacs.
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
;;; Commentary:
;; An alternative implementation of `make-process' for methods in
;; tramp-sh.el and tramp-adb.el. It does not use shell commands for
;; execution of the asynchronous command. Instead, it calls the
;; command directly. This should result in a performance boost.
;;
;; Limitations of this approach:
;;
;; * It works only for connection methods defined in tramp-sh.el and
;; tramp-adb.el.
;;
;; * It does not support multi-hop methods.
;;
;; * It does not support user authentication, like password handling.
;;
;; * It does not support a separated error stream.
;;
;; * It cannot be killed via `interrupt-process'.
;;
;; * It does not report the remote terminal name via `process-tty-name'.
;;
;; * It does not set environment variable "INSIDE_EMACS".
;;
;; In order to gain even more performance, it is recommended to set or
;; bind `tramp-verbose' to 0 when running `make-process'.
;;; Code:
;; We use BUFFER also as connection buffer during setup. Because of
;; this, its original contents must be saved, and restored once
;; connection has been setup.
(defun tramp-make-process (&rest args)
"An alternative `make-process' implementation for Tramp files."
(when args
(with-parsed-tramp-file-name (expand-file-name default-directory) nil
(let ((name (plist-get args :name))
(buffer (plist-get args :buffer))
(command (plist-get args :command))
(coding (plist-get args :coding))
(noquery (plist-get args :noquery))
(connection-type (plist-get args :connection-type))
(filter (plist-get args :filter))
(sentinel (plist-get args :sentinel))
(stderr (plist-get args :stderr)))
(unless (stringp name)
(signal 'wrong-type-argument (list #'stringp name)))
(unless (or (null buffer) (bufferp buffer) (stringp buffer))
(signal 'wrong-type-argument (list #'stringp buffer)))
(unless (consp command)
(signal 'wrong-type-argument (list #'consp command)))
(unless (or (null coding)
(and (symbolp coding) (memq coding coding-system-list))
(and (consp coding)
(memq (car coding) coding-system-list)
(memq (cdr coding) coding-system-list)))
(signal 'wrong-type-argument (list #'symbolp coding)))
(unless (or (null connection-type) (memq connection-type '(pipe pty)))
(signal 'wrong-type-argument (list #'symbolp connection-type)))
(unless (or (null filter) (functionp filter))
(signal 'wrong-type-argument (list #'functionp filter)))
(unless (or (null sentinel) (functionp sentinel))
(signal 'wrong-type-argument (list #'functionp sentinel)))
(unless (or (null stderr) (bufferp stderr) (stringp stderr))
(signal 'wrong-type-argument (list #'stringp stderr)))
(when (and (stringp stderr) (tramp-tramp-file-p stderr)
(not (tramp-equal-remote default-directory stderr)))
(signal 'file-error (list "Wrong stderr" stderr)))
(let* ((buffer
(if buffer
(get-buffer-create buffer)
;; BUFFER can be nil. We use a temporary buffer.
(generate-new-buffer tramp-temp-buffer-name)))
(command (append `("cd" ,localname "&&")
(mapcar #'tramp-shell-quote-argument command)))
(bmp (and (buffer-live-p buffer) (buffer-modified-p buffer)))
(name1 name)
(i 0)
;; We do not want to raise an error when `make-process'
;; has been started several times in `eshell' and
;; friends.
tramp-current-connection
p)
(while (get-process name1)
;; NAME must be unique as process name.
(setq i (1+ i)
name1 (format "%s<%d>" name i)))
(setq name name1)
;; Set the new process properties.
(tramp-set-connection-property v "process-name" name)
(tramp-set-connection-property v "process-buffer" buffer)
(with-current-buffer (tramp-get-connection-buffer v)
(unwind-protect
(let* ((login-program
(or (tramp-get-method-parameter v 'tramp-login-program)
"adb"))
(login-args
(or (tramp-get-method-parameter v 'tramp-login-args)
'(("shell"))))
(async-args
(tramp-get-method-parameter v 'tramp-async-args))
;; We don't create the temporary file. In
;; fact, it is just a prefix for the
;; ControlPath option of ssh; the real
;; temporary file has another name, and it is
;; created and protected by ssh. It is also
;; removed by ssh when the connection is
;; closed. The temporary file name is cached
;; in the main connection process, therefore
;; we cannot use `tramp-get-connection-process'.
(tmpfile
(with-tramp-connection-property
(tramp-get-process v) "temp-file"
(make-temp-name
(expand-file-name
tramp-temp-name-prefix
(tramp-compat-temporary-file-directory)))))
(options (tramp-ssh-controlmaster-options v))
spec)
;; Replace `login-args' place holders.
(setq
spec (format-spec-make ?t tmpfile)
options (format-spec options spec)
spec (format-spec-make
?h (or host "") ?u (or user "") ?p (or port "")
?c options ?l "")
;; Add arguments for asynchronous processes.
login-args (append async-args login-args)
;; Expand format spec.
login-args
(tramp-compat-flatten-tree
(mapcar
(lambda (x)
(setq x (mapcar (lambda (y) (format-spec y spec)) x))
(unless (member "" x) x))
login-args))
;; Split ControlMaster options.
login-args
(tramp-compat-flatten-tree
(mapcar (lambda (x) (split-string x " ")) login-args))
p (apply
#'start-process
name buffer login-program (append login-args command)))
(tramp-message v 6 "%s" (string-join (process-command p) " "))
;; Set sentinel and filter.
(when sentinel
(set-process-sentinel p sentinel))
(when filter
(set-process-filter p filter))
;; Set query flag and process marker for this
;; process. We ignore errors, because the
;; process could have finished already.
(ignore-errors
(set-process-query-on-exit-flag p (null noquery))
(set-marker (process-mark p) (point)))
;; We must flush them here already; otherwise
;; `rename-file', `delete-file' or
;; `insert-file-contents' will fail.
(tramp-flush-connection-property v "process-name")
(tramp-flush-connection-property v "process-buffer")
;; Return process.
p)
;; Save exit.
(if (string-match-p tramp-temp-buffer-name (buffer-name))
(ignore-errors
(set-process-buffer p nil)
(kill-buffer (current-buffer)))
(set-buffer-modified-p bmp))
(tramp-flush-connection-property v "process-name")
(tramp-flush-connection-property v "process-buffer"))))))))
(with-eval-after-load 'tramp-adb
(defalias 'tramp-adb-handle-make-process #'tramp-make-process))
(with-eval-after-load 'tramp-sh
(defalias 'tramp-sh-handle-make-process #'tramp-make-process))
(add-hook 'tramp-unload-hook
(lambda ()
(unload-feature 'tramp-make-process 'force)))
(provide 'tramp-make-process)
;;; tramp-make-process.el ends here
next prev parent reply other threads:[~2020-05-06 11:59 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2020-04-13 10:19 Remote asynchronous processes Michael Albinus
2020-04-13 20:32 ` Philippe Vaucher
2020-04-14 9:03 ` Michael Albinus
2020-04-14 12:34 ` Philippe Vaucher
2020-04-14 14:28 ` Michael Albinus
2020-08-04 17:00 ` Philipp Stephani
2020-04-14 14:48 ` Stefan Monnier
2020-04-14 15:30 ` Michael Albinus
2020-08-04 17:01 ` Philipp Stephani
2020-08-04 16:56 ` Philipp Stephani
2020-08-04 17:35 ` Michael Albinus
2020-08-10 14:42 ` Philipp Stephani
2020-05-06 11:59 ` Michael Albinus [this message]
2020-08-04 16:48 ` Philipp Stephani
2020-08-04 17:31 ` Michael Albinus
2020-08-10 14:56 ` Philipp Stephani
-- strict thread matches above, loose matches on Subject: below --
2020-07-29 16:58 Felipe Lema
2020-07-31 10:22 ` Michael Albinus
2020-08-04 12:27 ` Michael Albinus
2020-08-04 16:06 ` Felipe Lema
2020-08-06 19:08 ` Sean Whitton
2020-08-09 7:22 ` Michael Albinus
2020-08-09 14:47 ` Philipp Stephani
2020-08-09 17:06 ` Michael Albinus
2020-08-12 10:46 ` Michael Albinus
2020-08-21 22:28 ` Sean Whitton
2020-08-07 16:28 ` Philipp Stephani
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87a72ltgt5.fsf@gmx.de \
--to=michael.albinus@gmx.de \
--cc=emacs-devel@gnu.org \
--cc=p.stephani2@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).