PythonでWebAssemblyを実装してみる #Python #WebAssembly #WASM #フロント開発
この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
ブラウザ上でJavaScript以外の言語が動くらしい?
WebAssembly(WASM)という技術を使うと、ブラウザ上でJavaScript以外の言語を動かせるらしい。しかもJavaScriptよりも高速という噂だ。慣れ親しんだPythonで、Webのフロント開発をしたり、ブラウザ上でAIを動かしたりできるかもしれない。
そのような期待から、WebAssemblyの概要・仕組みや、ブラウザ上でPythonを動かす方法などについて調べてみることにした。実際に動作するサンプルプロジェクトも紹介する。
この記事で身につくこと
- WebAssemblyの概要の理解。
- WebAssemblyでPythonを使うためのアーキテクチャーの理解。
- WebAssemblyでPythonを使う際の可能性や制限などの理解する。
- WebAssemblyで動作する簡単なPythonプログラムを実装できるようになる。
この記事で扱わないこと
- Python以外でWebAssemblyを実装する方法。
この記事の前提知識:
- プログラミング一般に関する中級程度の知識。
- Pythonの基本。
- HTML・JavaScriptの基本。
- Web技術の基本。
WebAssemblyとは
まずは、WebAssemblyそのものについて調べていこう。
ウィキペディア・MDN・@ITなどを読みあさると、WebAssenblyは概ね以下のような技術であることが分かる。
- 主にWebフロントエンド高速化のために用いられる技術。JavaScriptより高速。
- WebAssemblyの実体は、各種プログラミング言語(主にC++)のコンパイル先として指定される、仮想命令セット。
- ファイルI/OなどのシステムコールやDOMアクセスなどは提供されない。
- 静的型付け。GCは提供されない。
- コンパイル結果は*.wasmファイル。
- 様々なランタイム(仮想マシン)上で実行される。主要ブラウザもWebAssemblyに対応している。
- ブラウザで実行する場合は、JavaScriptのWebAssembly JavaScript APIで*.wasmの内容を読み込む。
- JavaScriptからWebAssemblyの関数を呼び出すこと、またはその逆も可能。WebAssemblyからのDOMアクセスなどは、この相互やりとりを利用する。
つまり、C++などの言語で作成したプログラムをWebAssemblyにコンパイルし、生成された*.wasmファイルをJavaScriptで読み込むことによってブラウザ上で動作するという仕組みということだ。JavaScriptを一切知らなくてもよいということにはならないらしい。
いったん仮想命令にコンパイルしておいて実行時に仮想マシン上で実行されるという流れは、Javaの中間言語(バイトコード)&Java仮想マシン(Java VM)の関係と同じと理解すれば分かりやすいだろう。あるいは.Netの共通中間言語(CIL)&共通言語ランライム(CLR)の関係とも同じである。
WebAssemblyとPython
PythonからWebAssemblyにコンパイルすることはできない
WebAssemblyの概要が分かったので、今度はC++ではなくPythonで書けるのかという観点で調べていこう。そもそもPythonはコンパイル型ではなくインタープリター型だが、もしPythonのソースコードを*.wasm形式に変換できるのであれば、コンパイル型でないことはどうでもよい。
しかしながら、残念なことにPythonをWebAssemblyにコンパイルすることはできない。WebAssemblyは静的型付けであることやGCが存在しないなどの理由で、動的型付けでGCが存在するPythonとは非常に相性が悪いのだ。
C言語で書かれているPythonインタープリターをWebAssembly化して、その上でPythonを動かす
諦めるのは早い。Pythonの実行環境(=Pythonランタイム=Pythonインタープリター)をWebAssemblyにコンパイルして、その上で自前のPythonコードを動かせばよいのだ。既にいくつかの選択肢が提供されている(参考)が、最も有名なものはPyodideだろう。これは、一般によく使われているPython実行環境であるCPythonをWebAssemblyにコンパイルしたものだ。つまり、WindowsやMacでCPythonを動かす代わりにブラウザ上でCPythonを動かすのだ。あとは普段通りPythonのソースコードをPyodide(CPython)に読み込ませれば、ブラウザ上でPythonのプログラムが動作することになる。
繰り返しになるが、Pythonプログラム自体をWebAssembly化するわけではない。自作したPythonのソースコードは、実行するその時までPythonのソースコードのままだ。これが意味することは「確かにブラウザ上でPythonが動作するが、実行速度が懸念になる。」だろう。そもそもWebAssemblyは高速化のために発明された技術のはずだ。ここをクリアできなければ、動作するとは言え利用価値が非常に弱まってしまう。実行速度については改めて後の章で取り扱う。
PyodideとJavaScript
さて、先述の通りWebAssemblyではJavaScriptのAPIを用いて*.wasmファイルを読み込んだりJavaScriptとWebAssemblyとで相互のやりとりをするのが原則だ。だが、Pyodideを利用する場合にはその部分もPyodideがラッピングしてくれているため、通常はWebAssembly JavaScript APIを直接使う必要はなくなる。
コード例
簡単なコード例を紹介しておこう。
- JavaScriptからPythonにアクセス:
pyodide.runPython(code); // Pythonコードを実行。
pyodide.globals.get('x'); // Pythonの変数(関数も可)をJavaScriptから参照。
pyodide.globals.set("x", 'x will be now string'); // Pythonの変数をJavaScriptから更新。
pyodide.globals.set("alert", alert); // JavaScriptのalertをPythonで使えるようにする。
- PythonからJavaScriptにアクセス:
js.document.createElement("div") # PythonからDOMにアクセス。
サンプルプロジェクト
実際に動作するサンプルのプロジェクトを用意した。
プログラムの内容は、ブラウザ上のボタンが押されたら、PythonとJavaScriptで相互にやりとりするというものである。全体で50行程度の簡単なものだが、WebAssembly上のPythonで何ができるのか、どのように書けばよいのかを具体的に理解することができるだろう。
ダウンロードはこちらから。
ソースコードの他、実行手順や確認済みの動作環境に関するドキュメントを含めてある。
使い道
ここまでで、ブラウザ上でWebAssemblyを用いてPythonを動かす仕組みを理解することができた。次は、これがどのような場面で役立つかを考え、あわせて留意点についても検討していこう。
ユースケース案
ブラウザ上でPythonを動かすことができるという特徴から、以下のようなユースケースが思いつく。
- 既存のPythonプログラムのうち一部または全部をクライアント上で実行することにより、プログラム資源を再利用する。状況によりJavaScriptと併用する。
- Pythonに精通しているがJavaScriptには精通していないエンジニアが、クライアント上で動作する簡単なプログラムを作成する。
- 機械学習の推論処理をクライアント上で実行することにより、HTTPのオーバーヘッドを排除する。(結果として高速になるかは怪しい。)
留意点(+対応策)
WebAssemblyでPythonを動かす場合は、実行環境のアーキテクチャーから、以下のような点に留意する必要がある。
- Pyodideでサポートされているパッケージだけが利用できる。
- C言語による拡張パッケージは代表的なもの(NumPyなど)だけがサポートされている。
- 一部の標準ライブラリは使えない。純粋なPythonパッケージはサポートされている。
- 対応策:サポートされているパッケージまたは他の言語を使う。もしくはPyodide以外のPython実行環境をWebAssembly化したものを使う。
- ソースコードが露出する。
- ブラウザ上にPythonのソースコードを読み込ませるため、実行するユーザーにPythonのコードが見えてしまう。(JavaScriptと同様。)
- 対応策:PyArmorなどの難読化ツールを利用する。
- 実行速度が遅い。
- 公式によると、ネイティブPythonより3~5倍遅い。
- CのWebAssemblyはネイティブより2~2.5倍遅い。
- JavaScriptとどちらが速いかは不明。
- 対応策:速度が求められる部分はPython以外のWebAssemblyやサーバーサイドで実装する。
- 公式によると、ネイティブPythonより3~5倍遅い。
- Pythonの新しいバージョンが使えない。
- 現時点のPyodide最新版(0.22.0)はPython 3.10.2ベース。
- Python 3.10系の最新版は3.10.9、Python全体での最新版は3.11.1。
- パッチバージョンも古いため、セキュリティ上の懸念にもなる。
- 対応策:CPythonの最新版を自分でビルドする。
- 現時点のPyodide最新版(0.22.0)はPython 3.10.2ベース。
まとめ
- ブラウザ上でPythonを動かすことはできるが、速度などの点で注意が必要。
- WebAssemblyの「ブラウザ上でプログラムを高速に実行する」という特徴を活かすためにはPython以外の言語を用いるべき。
- Pythonを高速に動かすためにはWebAssemblyではなくサーバー上での実行が現実的。
- 速度以外に、ソースコードの露出やセキュリティ上の懸念もあることから、商用・大規模な開発では採用しにくい。
- WebAssemblyにUIフレームワークがあるわけではないので、必要に応じてReactなどを併用することになる。
- Pythonだけでフロント開発を完結させることは困難。