TRAMPのファイル転送が遅くて困る
EmacsにはTRAMPというリモートのファイルをローカルファイル同様に透過的に扱えるパッケージが標準で添付されています。開発中にサーバ上のファイルを操作する時などに大変便利で、わたしも普段から愛用しています。
しかし一方で、サーバから数MBのファイルをコピーしてくるだけでEmacsが分単位で固まるなど、速度の面で不満に感じることもちらほらありました。elispがあまり速くないとは言えこの速度はさすがに異常では…と思い調べてみると、使い方をすこし変えるだけで劇的に転送速度を改善することができたのでここにメモしておきます。
/ssh:ではなく/scp:を使うと速い
結論から先に書くと、リモートサーバ側のパスを/ssh:example.com:/path/to/dir
のように/ssh:
で指定するのをやめ、/scp:
に変えただけでファイル転送が劇的に速くなりました。
以下は某VPSの一番安いインスタンスから4.9MBのファイルをダウンロードして検証した結果です。/ssh:
で平均196秒かかっていた転送処理が、パスの指定を/scp:
に変えるだけで平均2.4秒で終わるようになりました。試しに/rsync:
も使ってみるとこちらも平均1.7秒で、/ssh:
と比較して100倍近い速度が出ていることがわかります。
;; パスの指定に/ssh:を使用した場合
(benchmark-run (copy-file "/ssh:host1:sample.zip" "~/sample.zip"))
; 3回実行した結果
; (209.477159 313 10.917686999999958)
; (172.97826 267 9.396021000000019)
; (208.477551 310 10.92040199999991)
;; パスの指定に/scp:を使用した場合
(benchmark-run (copy-file "/scp:host1:sample.zip" "~/sample.zip"))
; 3回実行した結果
; (2.55923 0 0.0)
; (2.370804 0 0.0)
; (2.198924 0 0.0)
;; パスの指定に/rsync:を指定した場合
(benchmark-run (copy-file "/rsync:host1:sample.zip" "~/sample.zip"))
; 3回実行した結果
; (2.113792 0 0.0)
; (1.989517 0 0.0)
; (1.79146 0 0.0)
外部転送メソッド
上記の例では、パスの指定を/ssh:
ではなく/scp:
とすることで、TRAMPの外部転送メソッドとしてscpを使用するようにしています。TRAMPのマニュアルによると、外部転送メソッドとはコマンドの実行にはssh、ファイルオープンやコピーの際の転送にはscp、のように接続を使い分ける仕組みのようです。/scp:
の他には/rsync:
や/sftp:
なども指定できます。
一方の/ssh:
はインラインメソッドというもので、コマンドの実行とファイルの転送を同じsshコネクションを使用してインラインで行うとのことでした。ファイル転送の際はuuencodeやbase64などでいったんテキストにして送受信するため、外部転送メソッドを使用する場合に比べてどうしても時間がかかってしまいます。
外部転送メソッドが使えないケース
良いことづくめな外部転送メソッドですが、またまたマニュアルによると転送に使用するコマンドで対話的にパスワード入力が求められる場合は使えないという制限があるとのことでした。
ということは鍵認証ではなくパスワード認証でサーバに接続しているようなケースだと使えないのでは?と思って試してみたのですが、なぜかパスワードを聞かれることなくscpできてしまった気がします。このあたりはsshのControlMasterの設定なども関連するかもしれませんが、面倒になってあまり調べてないです。
まとめ
長くなりましたが、/scp:
や/rsync:
などの外部転送メソッドが使える状況ならそちらを使った方がだいぶ速いよ、というお話でした。
今回の記事の内容に加えて、TRAMPとdiredを使ってローカルとサーバのディレクトリを左右に並べるとファイルコピーなどが大変捗るので超おすすめです。