ghq list -pをRustで200倍速くする
そもそもghqって?
ghqはManage remote repository clones
してくれる君です。
今はGOPATHとか設定しなくてもGolangは上手く動くようになったようですが、昔はGOPATH配下にソースコードを置いておく必要がありました。
その名残で現在私のソースコードはという感じのディレクトリ構成で管理されています。
/Users/serinuntius/src/github.com/**
そこでそのパス通りにインストールするときに楽なのがghqです。
ghq get <repo>
ってやると、良い感じのパスにcloneしてくれます。
ghq list -pとは?
ghq list -p
はローカルのリポジトリ一覧をフルパスで出力してくれる君です。
/Users/serinuntius/src/github.com/hoge
/Users/serinuntius/src/github.com/fuga
/Users/serinuntius/src/github.com/piyo
/Users/serinuntius/src/github.com/poyo
パスを出力してくれるので、pecoというcliに食わせてフィルタリングして、リポジトリのディレクトリ移動に利用しています。ghqでリポジトリ管理とpeco連携で快適git生活が参考になります。
手元には134のリポジトリもあるので、実行に時間がかかるようになってしまいました。
ghq version 1.1.5 (rev:HEAD)のベンチマークを回してみると
hyperfine 'ghq list -p'
Benchmark #1: ghq list -p
Time (mean ± σ): 1.185 s ± 0.334 s [User: 1.347 s, System: 10.284 s]
Range (min … max): 1.006 s … 2.128 s 10 runs
という感じで、1秒以上時間がかかっています。リポジトリを移動するたびに実行するコマンドなので、もう少し速くしたいですね!
Rustでghq list -pっぽいことしてくれるツールを作った
今Rustの勉強をちょうどしていて、やってこることは正直ただのシェルでもできちゃうぐらい簡単なものなので、存在価値はほとんどありませんw
なんとなく、素振りしたかっただけですw
ghq list -p
では色々重複チェックとか、バージョン管理システムを使っているディレクトリか等チェックしているようですが、自分のメインの用途では少しオーバースペックだと感じました。
とりあえず、最速で動かしたいので、指定のディレクトリを掘っていくだけのツールを書きました!
並列処理もしていないシンプルな処理ですが、とりあえず191倍速くなりました。
これはRustのおかげで速くなったわけではないと重々承知しております。
ghqの作者様たちには感謝しております。
Rust学習2日目に書いたので、色々と至らない点はあると思いますがソースコードです。
use std::env;
use std::path::PathBuf;
use std::io::{stdout, BufWriter, Write};
fn base_path() -> PathBuf {
match env::var("PACIFICA_PATH") {
Ok(path) => PathBuf::from(path),
Err(_) => PathBuf::from(env::var("HOME").expect("failed to read $HOME. You have to set PACIFICA_PATH.") + "/src"),
}
}
fn walk_dir(path: PathBuf, depth: i8) {
// buffering
let out = stdout();
let mut out = BufWriter::new(out.lock());
let paths = path.read_dir().expect("failed to read dir");
for p in paths {
let pb = p.unwrap().path();
let path_string = pb.display().to_string();
if !pb.is_dir() {
continue;
}
if depth - 1 >= 0 {
walk_dir(pb, depth - 1)
} else if !path_string.split("/").last().expect("failed to path_string.split(\"/\").last()").starts_with(".") {
// prevent something like a .git coming in.
write!(out, "{}\n", path_string).expect("failed to write stdout");
}
}
}
fn main() {
let base_path = base_path();
walk_dir(base_path, 2);
}
ベンチマーク
hyperfine './target/release/pacifica'
Benchmark #1: ./target/release/pacifica
Time (mean ± σ): 6.2 ms ± 0.5 ms [User: 1.5 ms, System: 3.6 ms]
Range (min … max): 5.4 ms … 8.3 ms 325 runs
hyperfine 'ghq list -p'
Benchmark #1: ghq list -p
Time (mean ± σ): 1.185 s ± 0.334 s [User: 1.347 s, System: 10.284 s]
Range (min … max): 1.006 s … 2.128 s 10 runs
6.2msなので体感的には一瞬で全くストレスはありません。
pecoでディレクトリ移動するやつ
ctrl + ]
でpecoで移動できます。
function peco-src () {
local selected_dir=$(pacifica | peco --query "$LBUFFER")
if [ -n "$selected_dir" ]; then
BUFFER="cd ${selected_dir}"
zle accept-line
fi
zle clear-screen
}
zle -N peco-src
bindkey '^]' peco-src
まとめ
普段使っているツールを自分の用途に合わせて高速にするのは楽しいですね。not for meの人もいるとは思いますが、遅くて困っている人は使ってみてはいかがでしょうか?
Discussion