2024.12.24
ビジネスが急速に変化する現代は「OODAサイクル」と親和性が高い 流通卸売業界を取り巻く5つの課題と打開策
Rustでネイティブモジュールを書く(全1記事)
リンクをコピー
記事をブックマーク
Yukimasa Funaoka氏(以下、Funaoka):まず、自己紹介をします。私は、株式会社ディー・エヌ・エーでフロントエンドの業務をしている、船岡と申します。
本日は、どういった目的でネイティブモジュールを書くのか、具体的にRustで作るにはどうすればよいのか、そしてクロスプラットフォーム対応と、実際に書いてみて困ったことなどを紹介したいと思っています。
まず、ネイティブモジュールを書く理由ですが、ネイティブモジュールは、JS(JavaScript)の世界を超えて、ネイティブのバイナリとNode(Node.js)との架け橋になって、実行できます。
ネイティブモジュールのメリットとして、まず処理速度が挙げられると思います。
最近はesbuildが話題ですが、以前からあるものだと画像処理ライブラリのsharpなども、速度を重視したネイティブモジュールとして使われています。
次に、移植性です。C++で書かれたライブラリだと、JSで書き直すとけっこう大変ですが、ネイティブモジュールであれば、簡単にNodeから使えるというメリットがあります。node-sassは、内部的にはLibSassという、C++のライブラリを利用して書かれています。
最後に、シリアル通信のNode Serialportライブラリなど、OS準拠のネイティブからしか扱えない機能を利用できるというメリットがあります。
ほかには、GPUを使うケースや、JSから扱えない機能を使いたい時は、ネイティブモジュールを使う必要があります。
今回は、処理速度を上げるためのネイティブモジュールを書きました。実際のコードは出せませんが、代わりにサンプルを用意しています。RustのImageライブラリで、画像を読み込んで、rgbaのExcelデータを返すシンプルなものです。サンプルリポジトリと資料は、(スライドの)左下のURLにあります。
それでは実際に、napi-rsというライブラリを使って、Rustでネイティブモジュールを作っていきます。
以前のネイティブモジュールは、生のV8のコードを書く必要があり、Nodeのバージョンが変わるたびに使えなくなったりして、使いづらいものでした。
今では、名前が変わってNode-APIと呼んだりすることがありますが、これが後方互換のあるインターフェイスを実装しているため、すごく便利に使えようになりました。これをRustから作れるようにしたものが、napi-rsというライブラリです。
具体的には、ビルドや、各種ワークフローを実行するnapi/cli(Node.js)。Rustからnapiを使うためのcrate、Nodeでのパッケージに当たる、napiとnapi-deriveがあります。
シンプルな使い方としては、napi newを実行すると、initのように、さまざまなもののファイルが生成されます。このあたりはドキュメントを見れば、すぐにわかると思うので、今回は実際のコードでポイントになる部分を紹介したいと思います。
まず、Nodeのパッケージ実装に当たる、Cargo.tomlというファイルについて説明します。crate-typeは、ふだんRustではあまり書かないものだと思いますが、これを”cdylib”として動的リンクライブラリで、かつ他言語から扱えるようにビルドしています。
依存関係については、napiのバージョンのところで、featuresというオプションを指定できます。ここで、ターゲットになるnapiのバージョンや拡張機能を設定できます。
これについてはドキュメントがありますので、そちらを参照してください。
ライブラリ本体のコードには、napi_deriveにderiveという、#で書く部分の実装があります。これはjs_functionという関数定義で、括弧の数値は引数の数を示しています。実際に何個の引数で呼び出されたかは、コンテキストのlengthプロパティを参照することで確認できます。
もう1つが、module.exportsで、これはいわゆるJSでおなじみのmodule.exportsそのままです。wasmで書く時には、上のjs_functionを書くだけで済み、module.exportsを書く必要がありません。wasmで書く場合とnapiを使う場合ではちょっと違うため、忘れるという罠にハマりがちだと感じました。
あとは、napi buildというコマンドを叩けば、実際にNodeから使えるネイティブモジュールが完成します。基本的な作り方としては、以上です。
クロスプラットフォームの対応は、ネイティブモジュールはやはりネイティブバイナリなので、基本的にはビルドしたプラットフォームに依存します。例えば、Linuxの64bitでビルドしたものは、Linuxの64bitでしか使えません。
ただ、インストールのたびにビルドするのは時間がかかるし、そもそもビルド環境が整っていなければインストールすらできないこともあります。実際にnpmを実装した時に、エラーでこけるケースを体験された方も多いのではないでしょうか。
これに対する対処として、バイナリの配布があります。全部突っ込むというのは、node_modulesが太ってしまうし、あまりよくありません。
postinstallなどで、インストールする時にダウンロードする方法もありますが、ローカルでレジストリを運用していたり、インターネットを使ってはいけない環境だったりで、インストールできない場合があります。
そのほかに、optionalDependenciesを利用する方法がありますので、今回はこれを使いたいと思います。optionalDependenciesはインストールを試みて、失敗したらスキップするという挙動をします。
そのため、各プラットフォーム用のパッケージを並べておくことで、別のプラットフォーム用のパッケージがすべてスキップされて、今のプラットフォーム用のパッケージだけがインストールされる挙動になります。
ただ、これだけでは十分ではなくて、想定外のプラットフォームからインストールされることも考えられます。これに対しては、諦めてインストール時にビルドするという手もありますが、そのほかにwasmを用意しておいて、これにfallbackする方法もあります。ネイティブの機能を使う場合はできませんが、今回は後者で対応することにしました。
これをwasmで書くことで、ブラウザーで動くライブラリにできるので一石二鳥です。ネイティブに比べて速度面では劣りますが、ここは妥協します。
wasmの書き方については、今回Rustの話ということで割愛します。ただ、具体的にこれを実現するためには、ライブラリ本体とnapi用とwasm用の、3つのcrateを用意するのがシンプルです。
napiやwasmから、ライブラリ本体のcrateを呼び出す場合は、バージョンを書くところに相対パスでパスを指定します。それによってローカルのcrateが作成できるので、分割できます。
今回、napiをRustで書く際のnapi-rsの使い方、書き方がわからないため、とても困りました。サンプルコードがないし、なんならテストすらないし。一部のメソッドは、GitHub全体でコード検索しても、使用例が数えるほどしかないという、すごく悲しい状況になっています。
今回は、仕方なくソースコードを見ながら実装しましたが、napiを初めて触る人だと、けっこう大変かもしれないなと感じました。
crateがnot foundというエラーになってしまって、テストが書けないという問題も発生しました。これはcrate_typeをcdylibにしたことが原因で、Rust用のcrate_typeがない場合は、rustcが定義を見つけられないため、crateが見つからないというエラーになってしまいます。
これについてはRust用のライブラリのデフォルトである、rlibというcrate_typeを追加することで解決できます。
最後に、Error: Module did not self-registerというエラー。これはNode.jsからrequireした時に出るエラーですが、ググるとまったく別の要因のものばかり出てきて、すごくわかりづらいため、特定するのに非常に時間がかかりました。
これは先ほども述べましたが、module_exportsが足りていない場合に発生するエラーです。同じエラーでハマった人は、ぜひこれを参考にしてもらえればと思います。
最後にまとめです。Rustでネイティブモジュールを作るのは、現状けっこう大変だと感じました。C++で書くnapiは事例もあって、サンプルコードもそこらへんに転がっていてすごく楽なので、今書くのであればまだC++のほうがちょっと楽だと感じました。
ただ、最初に言ったように、ネイティブモジュールのメリットは非常に大きいものがあります。機会があればぜひ書いてみてもらいたいと思います。
以上で発表を終わります。ありがとうございました。
司会者:ありがとうございました。まだRustでnapiを書くこと自体を、やられている人は少ないのでしょうね。C++は、公式ドキュメントとかに、内容がありますが。
Funaoka:そうですね。
司会者:でも、非常にチャレンジングなことをされていておもしろかったです。もし機会があれば、Rustでネイティブバインドしたやつと、WebAssemblyにしたやつで、それぞれ試した時にどちらがどれだけいいとか、ビルド時間はどっちが速いかなども含めて、検証できるとおもしろいかもなと思いました。というか、やってみたいなと思いました(笑)。
Funaoka:そうですね。iswasmfastを見るとどっちが速いか、みたいなことをやっていますが、場合によるって感じですね。
司会者:実行時間でどっちが速いかも気になりますが、ビルド時間とかも含めると、どっちが速いのかなとかも気になっています。ありがとうございました。
Funaoka:ありがとうございました。
関連タグ:
2025.01.16
社内プレゼンは時間のムダ パワポ資料のプロが重視する、「ペライチ資料」で意見を通すこと
2025.01.15
若手がごろごろ辞める会社で「給料を5万円アップ」するも効果なし… 従業員のモチベーションを上げるために必要なことは何か
2025.01.09
マッキンゼーのマネージャーが「資料を作る前」に準備する すべてのアウトプットを支える論理的なフレームワーク
2025.01.14
コンサルが「理由は3つあります」と前置きする理由 マッキンゼー流、プレゼンの質を向上させる具体的Tips
2025.01.07
1月から始めたい「日記」を書く習慣 ビジネスパーソンにおすすめな3つの理由
2025.01.14
目標がなく悩む若手、育成を放棄する管理職… 社員をやる気にさせる「等級制度」を作るための第一歩
2025.01.10
プレゼンで突っ込まれそうなポイントの事前準備術 マッキンゼー流、顧客や上司の「意思決定」を加速させる工夫
2025.01.07
資料は3日前に完成 「伝え方」で差がつく、マッキンゼー流プレゼン準備術
2017.03.05
地面からつららが伸びる? 氷がもたらす不思議な現象
2025.01.08
職場にいる「嫌われた上司」がたどる末路 よくあるダメな嫌われ方・良い嫌われ方の違いとは
特別対談「伝える×伝える」 ~1on1で伝えること、伝わること~
2024.12.16 - 2024.12.16
安野たかひろ氏・AIプロジェクト「デジタル民主主義2030」立ち上げ会見
2025.01.16 - 2025.01.16
国際コーチング連盟認定のプロフェッショナルコーチ”あべき光司”先生新刊『リーダーのためのコーチングがイチからわかる本』発売記念【オンラインイベント】
2024.12.09 - 2024.12.09
NEXT Innovation Summit 2024 in Autumn特別提供コンテンツ
2024.12.24 - 2024.12.24
プレゼンが上手くなる!5つのポイント|話し方のプロ・資料のプロが解説【カエカ 千葉様】
2024.08.31 - 2024.08.31