ぶていのログでぶログ

思い出したが吉日

reddish-shell 開発進捗 v0.11.0 | Rust化完了

満足するところまで作れたのでv.0.11.0をマージした。 Rustで作り直す前にはあった機能を一部実装していない*1があるが、一旦区切りをつけたかったのでマージした。 割り切り重要。Done is better than perfect. いや意味するところは違うと思うけど…。

前回の記事からの差分コミット:

clippyを有効化

VSCodeのRust extensionを使ってコーディングしていて、保存するときにフォーマッティングしてくれていたのでclippyが効いていると思いこんでいたのだが実はrustfmtが実行されているだけで、clippyが有効になっていなかった。 オプトインになっているようなのでVSCodeの設定から有効にし、指摘されたところを修正した -> a55d480。 なるほど〜ということが多いので有効にしておくとすっきりしたコードがかけると思うのでおすすめ。

mrubyを内蔵

reddish-shellではmrubyを内蔵して、Rubyコードが実行できることもコンセプトとしている。 Rust化してもそのコンセプトを残したかったのでRustからmrubyを呼び出せるようにした。 幸い先行実装(crate)があるのとRustでFFIするのが楽だった*2。 しかし、今回はすでにあるcrateは使わず自前で実装した -> buty4649/rust-mruby: mruby bindings for Rust. すでにあるcrateはbuild_config.rbが使えなかったり、使用しているmrubyが古かったりして絶妙マッチしなかったのが理由。 rust-mrubyはまだ大したことはできないが、build_config.rsを作って MRuby::new().exec_from_string("puts 'hello world', &[], None) とかすると動くので割と満足している。

mrubyでスクリプトを実行すると、呼び出し元から安全にmruby VMを停止させる方法がないためにCtrl-Cが聞かずに無限ループしてしまう問題がある。 今後実装したい機能として、sourceコマンドでRubyスクリプトを呼び出すことができシェルを初期化できるということをやりたいのだが、初期化スクリプトの中で無限ループしてしまうと外からkill -9なりするしかなくなるという…。 色々考えたりコードを修正してみたのだが安全に停止する方法がないので、諦めてforkすることにした。 終了するときは、mruby VMごとプロセスを破棄するという手法を取ることにした。 できないことを無理にやろうとしても辛いからね…。 今のところはこれで困っていないので、今後困ったら再度考えることにする。

実行速度の改善

Rustで再実装しようと思ったきっかけの1つに実行速度の遅さがあった。 再実装前(v0.10.0)は、bashと同じスクリプトを実行させると6倍以上遅いという結果になっていた。 今回の再実装により、bashより2.6倍程度遅いくらいまでには高速化されて満足している。 というかbashが早すぎる…。

ベンチスクリプト↓ builtinコマンドの実装による速度差をなくすためにOS標準のコマンドを使用している。

❯ cat bench.sh
#!/bin/bash

TEST=0

while /usr/bin/[ $TEST -ne 100 ]; do
    TEST=`/usr/bin/expr $TEST + 1`
done

使用するreddish-shellはv0.10.0(mruby+C実装)とv0.11.0(Rust実装)

❯ ./v0.10.0/reddish --version
reddish: 0.10.0

❯ ./v0.11.0/reddish --version
reddish 0.11.0

ベンチマークはhyperfineを使った。

❯ hyperfine --warmup 3 'bash bench.sh' './v0.10.0/reddish bench.sh' './v0.11.0/reddish bench.sh'
Benchmark 1: bash bench.sh
  Time (mean ± σ):      79.7 ms ±   2.3 ms    [User: 68.9 ms, System: 14.3 ms]
  Range (min … max):    76.4 ms …  85.0 ms    37 runs

Benchmark 2: ./v0.10.0/reddish bench.sh
  Time (mean ± σ):     491.1 ms ±  10.9 ms    [User: 451.8 ms, System: 43.2 ms]
  Range (min … max):   481.2 ms … 512.1 ms    10 runs

Benchmark 3: ./v0.11.0/reddish bench.sh
  Time (mean ± σ):     211.0 ms ±   3.2 ms    [User: 191.1 ms, System: 30.9 ms]
  Range (min … max):   206.0 ms … 217.1 ms    14 runs

Summary
  'bash bench.sh' ran
    2.65 ± 0.09 times faster than './v0.11.0/reddish bench.sh'
    6.16 ± 0.23 times faster than './v0.10.0/reddish bench.sh'

今後の予定

2ヶ月くらいかけて今まで作ったmruby+Cでの実装をRustで再実装できたのは満足している。 Rustも最初はとっつきにくいかと思ったけど、自転車本のおかげですんなりRustで開発できるようになってよかった。

これでreddish-shellの開発は終わり!というわけではなく、今まで登ってきた道をイチからやり直して戻ってきただけなので、「オレはようやくのぼりはじめたばかりだからな このはてしなく遠い自作シェル道をよ…」という感じ。 まずは、再実装前にあった機能を実装するのと、テストコードの追加をしたいと思う。 パーサーを作っているときにはテストコードを追加していたのだが、シェルの実行エンジンを作り始めてからシステムコールなど外部に依存する部分が増えテストが書きづらくなったのもありサボってしまった・・・。 言い訳はよくないのでガっとテストコードを追加してGitHub ActionsでCIするところまで設定したい。

*1:-Rオプションとかサジェスト機能とか

*2:自転車本にも解説が載っていて助かった