私は現在、Rust の Python インタープリターである Memphis について、WebAssembly 用のビルドと CPython の埋め込みという 2 つの興味深いトピックを検討しています。今週は報告すべき大きなマイルストーンがないので、進行中の考えをいくつか共有したいと思います。私にとって、メンフィスは、実践的な実験を通じて概念的な理解を広げるためのプロジェクトでした。私が検討している設計上の決定事項のいくつかを説明する際に、この投稿があなたにも同じように役立つことを願っています。
Memphis を WebAssembly ターゲットにコンパイルすることは、しばらく頭の片隅にありましたが、2 週間前の土曜日、ついに試してみました。コースターにドリップコーヒーのぬるま湯を乗せて、関節を鳴らして始めました。
WebAssembly は、従来の JavaScript 環境を補完する最新の Web ブラウザー内のサンドボックス実行環境です。 Wasm 環境はネイティブ コードに近く、よりパフォーマンスの高い CPU コンテキストの恩恵を受けるタスクに使用できます。計算の処理や愚かな忙しいループを考えてください。私はパフォーマンスの観点からではなく、むしろそれが可能であるという理由でそれに興味を持ちました。 Rust のセールスポイント (文字通り数十億のうちの) の 1 つは、Wasm をターゲットにできることです。どうやって行うのですか?と尋ねる人もいるかもしれません。これが可能なのは、Rust がコンパイラ バックエンドとして LLVM を使用しているためです。 Rust コンパイラ フロントエンドは LLVM 中間表現 (IR) コードを生成し、LLVM はこれを数十のターゲットのネイティブ コードにコンパイルできます。
これは非常に大きなメリットであり、メンフィスでもうまくいくかどうか興味がありました。私はこれまで、ブラウザーで Python を実行することについてまったく考えたこともなかったので、これは Wasm の学習曲線をテストする絶好の機会のように思えました。
私は AI アシスタントを起動して、起動シーケンスを尋ねました。ピーピーピーピーって鳴った。以下は、私がその過程で学んだことを注釈として付けた手順です。
# wasm-pack helps compile our Rust code to WebAssembly and bundle it # with JavaScript bindings we can call from our HTML/JavaScript page. cargo install wasm-pack # wasm-pack also downloads the wasm32-unknown-unknown target via # rustup for us. If for whatever reason it does not, you can use this: # rustup target add wasm32-unknown-unknown # We must specify a feature flag because our wasm_bindgen interface is # behind the wasm feature flag. wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
最初の試行でビルドが成功しました。ただし、Rust バイナリ内の関数を WebAssembly から呼び出し可能としてマークしていないため、あまり役に立ちません。
これを行うために wasm-bindgen クレートをインストールできます。これを機能フラグの後ろに置きました。これを Cargo.toml に追加しました。
[dependencies] wasm-bindgen = { version = "0.2", optional = true } [features] wasm = ["wasm-bindgen"]
これは、src/lib.rs ファイルの wasm 機能フラグの後ろに追加した小さなコードです。このシンボルを JavaScript で使用できるようにするために、greet 関数は #[wasm_bindgen] で修飾されています。
# wasm-pack helps compile our Rust code to WebAssembly and bundle it # with JavaScript bindings we can call from our HTML/JavaScript page. cargo install wasm-pack # wasm-pack also downloads the wasm32-unknown-unknown target via # rustup for us. If for whatever reason it does not, you can use this: # rustup target add wasm32-unknown-unknown # We must specify a feature flag because our wasm_bindgen interface is # behind the wasm feature flag. wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
また、AI アシスタントに、Wasm インターフェイスのテストに使用できる最小の JavaScript を要求しました。 init() を呼び出すと、ブラウザーは .wasm ファイルを読み込み、JIT コンパイル ステップを実行して移植可能な WebAssembly バイナリをネイティブ コードに変換し、WebAssembly ランタイムのメモリを初期化します。
[dependencies] wasm-bindgen = { version = "0.2", optional = true } [features] wasm = ["wasm-bindgen"]
奇跡の中の奇跡のように、それはうまくいきました。確かに、私はブラウザで Python コードを実行していませんでしたが、バイナリとのインターフェイスは、Java をかろうじてインストールできた若い私にとって過小評価したくなかった大きなステップでした。
次のステップは、JavaScript で定義された Python 式を与え、Wasm バイナリで数値を処理することでした。 REPL の投稿で述べたように、ソフトウェア プロジェクトのすべてのエントリ ポイントは抽象化を改善する機会であり、ここでも間違いなく当てはまります。メンフィスのリポジトリをざっと見てみると、すごい、文字列を渡して Python として評価するには、もっと良いインターフェイスが必要だということに気づきました。 先ほども言いましたが、私は新しいエントリ ポイントが大好きです。
当面はクロスチェックアダプターを使用します。 Crosscheck は、ツリーウォーク インタープリターとバイトコード VM が特定の Python 入力に対して同じ動作を生成することを検証するための、私が開発中のテスト フレームワークです。客室乗務員の仕事にちなんで名付けられました。
これが私の更新された Rust コードです。
#[cfg(feature = "wasm")] mod wasm { use wasm_bindgen::prelude::wasm_bindgen; // Export a function to JavaScript #[wasm_bindgen] pub fn greet() -> String { "Hello from WebAssembly!".to_string() } }
これは、新しい Rust 評価関数を呼び出す、更新された JavaScript コードです。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Wasm Test</title> </head> <body> <script type="module"> import init, { greet } from './pkg/memphis.js'; async function run() { await init(); console.log(greet()); } run(); </script> </body> </html>
実行すると……コンソール エラーが発生しました。未実装のエラーによりクラッシュしました。
少し調べてみましたが、何が原因なのかはわかりませんでした。ソースをクリックすることはできますが、Wasm ビルドは元の Rust 関数への参照のない単なるアセンブリのブロックです。
AI チャット/グーグルをいくつか行ったところ、2 つの役立つアプローチが見つかりました。 1 つは Wasm ビルドで使用する console_log で、Rust コードからのログ ステートメントをブラウザー コンソールに表示します。これはある程度役に立ちましたが、私が本当に探していたのはスタック トレースでした。 「console_error_panic_hook」と入力します。 Rust のスタック トレースがすぐに表示されました。それは CLUTCH でした。独自の Wasm ビルドを行っている場合は、今すぐこれを読むのをやめて、このクレートを追加してください。この記事を最後まで読み終わらなくても構いません。フェリスはあなたにこの箱を使ってほしいと思っていますか?これを Wasm インターフェースに追加する方法を示します。
#[cfg(feature = "wasm")] mod wasm { use wasm_bindgen::prelude::wasm_bindgen; use crosscheck::{InterpreterTest, TreewalkAdapter}; // Export a function to JavaScript #[wasm_bindgen] pub fn greet() -> String { "Hello from WebAssembly!".to_string() } #[wasm_bindgen] pub fn evaluate(code: String) -> String { let result = TreewalkAdapter.execute(&code); format!("{}", result) } }
私のスタック トレースは原因を突き止めました。私は std::env を使用していくつかの OS リソースをリクエストしていましたが、これは Wasm ランタイム (サンドボックス化された部分) では許可されていませんでした。これらの呼び出しを機能フラグの後ろに置き (これらは、ホスト マシン上の Python 標準ライブラリの場所をハック的に決定する方法に関連しています)、ビルドを再度起動しました。戻り値の型を適切に表示することに関連したいくつかの小さな失敗の後….
うまくいきました。これがブラウザコンソールに表示される内容です。
# wasm-pack helps compile our Rust code to WebAssembly and bundle it # with JavaScript bindings we can call from our HTML/JavaScript page. cargo install wasm-pack # wasm-pack also downloads the wasm32-unknown-unknown target via # rustup for us. If for whatever reason it does not, you can use this: # rustup target add wasm32-unknown-unknown # We must specify a feature flag because our wasm_bindgen interface is # behind the wasm feature flag. wasm-pack build --target web --out-dir wasm_ui/pkg -- --features wasm
tldr ブラウザーで Python を実行できます。 (彼らの名誉のために言っておきますが、RustPython もこれを行っています: https://rustpython.github.io/demo/。私は彼らのプロジェクトを詳しく見ていませんが、包括的なようです。) Python のリスト内包表記は JavaScript で文字列形式で定義されており、応答リストは、Wasm にコンパイルされた Rust コードによって評価され、JavaScript で表示できる文字列に変換されます。
現時点では、このセットアップでは式のみがサポートされています。ステートメントを評価するには (そして後でその結果を読み戻すには)、Rust 側で状態を保持する必要があります。私も JavaScript REPL を構築することを夢見ています。それは未来の私にとっては問題のように思えます (そして退屈な夢ですが)。
話が長くなったので、埋め込み Python については来週の月曜日まで議論しないことにします。
おとり商法で申し訳ありません。コンテンツ カレンダーは誰も待ちません。
明確にするために、埋め込み Python とは、Memphis 内に CPython インタープリターを埋め込むことを意味しており、「組み込みシステム」環境で Python を実行することではありません。それは理由もなく難しいでしょう。楽しいのが難しいメンフィスとは異なります。
このような投稿をさらに直接受信トレイに受け取りたい場合は、ここから購読できます!
私はソフトウェア エンジニアの指導に加えて、成人と診断された自閉症者としての経験についても書いています。コードは減り、ジョークの数は同じです。
以上がWebAssembly 用のビルドの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。