テストステ論

高テス協会会長が, テストステロンに関する情報をお届けします.

(nim-fuse) 設計判断: どこでlibfuseと決別するか?

nim-fuseに手を挙げた時, 私には1つの設計しか見えていなかった. しかし実際には深みがある.

例えば, 単純な数値計算ライブラリ(例:sin)をfuseでラップするのであれば, 設計は1つしかない. 単にFFIを使ってCを呼び出せばいい. しかしfuseのX言語バインディングを作るというのはそう単純なものではない. ポイントは, X言語側で実装した構造体を渡すところにある. これをX言語で書いた場合, 通常はlibfuseにこれを直接渡すことは無理である. Nimでは, C言語をターゲット言語としてコンパイルすることが可能であるから, 気合を入れれば出来ないことはないと思うのだが, ラッパーを自動生成とか, そういう技術が必要になると思う. これは恐らく, 失敗である.

以下に, 既存バインディングを調査した結果明らかとなった2つの実装方式について説明する.

javafuse方式(ジャバ

もっとも直感的には, 現在のlibfuseのフレームワークに発射台を作り, そこから強引にX言語を呼び出すという設計があり得る. この設計を採用しているのがjavafuseだ. javafuseでは, 各fuse opについて発射台を作っている. 以下のような感じ.

javafuse_some_op(args):
  fs = JVM->getObject(fs)
  JVM->call(fs, some_op)

実際にはこう簡単ではなくて, JVMを呼び出す部分も複雑だし, Javaで実装すべきインターフェイスも複雑になっている.

この方式の良い点は, 誤りにくい点だと思う. libfuseが外向けに提供している良くドキュメントされたインターフェイスをそのまま利用して, 他の部分はすべてlibfuseに任せているからである. 変更にも強いだろう. fuseが外向けのインターフェイスを今後変更するとは到底思えないからだ. 悪い点は, fuseに適合するために相互呼び出しが多く必要となることだ. 性能上のオーバーヘッドを無視したとしても, コードがシンプルにならない可能性がある. また最悪ケース, fuseの内部実装変化に追従することが結果として必要となってくる可能性がある.

rust-fuse方式

ざっくりいうと, fuseの重要な要素は, 以下の2点である.

  • 初期化: カーネルとのチャネルの確立
  • 処理: カーネルからリクエストを受け取って処理する

libfuseではカーネルとのやりとりにfdを取得し, それをラップしたオブジェクト(チャネル, セッション etc)を構築している. そのコードは意味もなく複雑である. GCや非同期処理機構のある言語ならばよりシンプルに書けることは明白である.

rustは, fdを取得/破棄する部分だけをFFIで呼び出して, その他の部分はすべてrustで実装するという方式を採用している. つまり, fdというプリミティブなものより上の実装はすべて潔く破棄する. カーネルから受け取るプロトコルが変更される可能性は低いため, これに依存したコードを書くことは悪くない. また, fdを介してカーネルとやりとりするという設計も変更になる可能性は低いので, これにも依存出来る.

この方式の良い点は, C言語との接点を極力少なく出来ることであるが, 悪い点は, libfuseに存在するコードを再インプリする必要があることである. また, fdのみに依存するという境界を見誤っていた場合, 実装が破綻する危険性がある(一方, javafuse方式は「絶対に実装出来る」).

結論

今のところ, rust-fuse方式が良いと思っている. すべてNimでコントロールした方が速いという矮小な理由ではなく, アーキテクチャとしてきれいになる可能性が高いからである. 実際, 外からリクエストが来ている場合に何かをするというアーキテクチャは, わりと枯れているので, 言語できれいにサポートされている可能性が高いし, そうでなくてもライブラリレベルでは存在する可能性が高い. つまり, 組み合わせで実装出来る可能性が高い. 組み合わせで実装することは良いことである. 理由は1)個々のコンポーネントが改善された時, その恩恵を受けることが出来る 2)読み手が理解しやすい rust-fuse方式は確かにCのコードの再実装を行う部分も多いと思うが, そもそもCのコード自体が既存技術の再実装なのであるからそれは再実装ではないということが出来る.

プロジェクトとしての最大の敵はrust-fuseか, 先行しているpythonバインディングだと思っている. rust-fuseと同じ設計を採用することは, 失敗の可能性を共有出来る利点がある. こちらが死ぬ時に敵も死ぬならば悪くない. また, 純粋に言語の学習容易性を考えた時, 両方学んだ私に評価させると, Nimの方が簡単である. Rustは, 広く受け入れられるためにはあまりに奇抜である. アプリケーションは言語好きが書くわけではないのでこれは良くない. pythonバインディングが成功しているのはここが理由だろう. pythonは簡単すぎる. nim-fusepythonバインディングに書くことはなかなか難しいが, なるべくオーバーヘッドを小さくしたいだとか, 静的型づけをしたいというニーズがあれば, 勝つことが出来るだろう.
rust-fuseの設計を基本にすることは, rust-fuseの開発者を取り込むことにも繋がる. さまざまな細かいフィックスなどを期待出来る. 実際, 私がそうであるように, rustを学ぶものはNimも学ぶので, この可能性は大いにある.

結論として, rust-fuseにもpythonバインディングにも勝つためには, rust-fuse方式を採用するのが無難なのである.