プログラミング
ブラウザ上のNumba: JupyterLiteで新しい科学的Pythonスタックを解き放つ
Numba in the Browser: Unlocking a New Scientific Python Stack in JupyterLite (notebook.link)
要約
JupyterLiteは、WebAssemblyを利用してブラウザ上でPythonカーネルを実行し、サーバー不要の計算環境を提供します。今回、この環境にNumbaのJITコンパイラが導入され、ブラウザ内で直接Pythonコードを高速化できるようになりました。これにより、科学計算の分野で、より手軽で安価なインタラクティブな開発が可能になります。
全文翻訳
JITコンパイラとそのエコシステムが、完全にWebブラウザで実行される科学者、学生、エンジニアは、Jupyter Notebookを使用してアイデアをインタラクティブに探求します。つまり、短いコードを書き、実行し、結果を検査し、そこから続けます。従来、このような各ノートブックには、サーバーまたはユーザーのコンピューターで実行されるPythonプロセスが必要でした。JupyterLiteはこのモデルを変更します。そのカーネルはWebAssemblyを介してWebブラウザでローカルに実行されるため、静的ウェブサイトは各ユーザーにサーバーを割り当てることなく、完全な計算環境を提供できます。これにより、ドキュメント、教育、またはインタラクティブなデモンストレーションに使用される場合でも、ノートブックの共有が容易になり、コストも削減されます。しかし、ブラウザベースの科学的Pythonエコシステムには、重要なピースが欠けていました。それがNumbaです。本日、JupyterLiteとemscripten-forgeを使用して、ブラウザで完全に実行されるNumba JITコンパイラの最初の動作バージョンを共有できることを嬉しく思います。JupyterLiteでのNumbaの動作。標準Pythonと比較して249倍の高速化を示しています。この例では、NumbaはWebAssemblyで約250倍の高速化を実現し、ネイティブでは約90倍です。相対的なゲインが大きいほど、Numbaはブラウザで特に魅力的になります。そこでは、Pythonインタプリタのオーバーヘッドを回避することがさらに大きな影響を与える可能性があります。これは、Python関数をNumbaのIntermediate Representation(IR)に変換し、型付けし、LLVM IRに低下させ、WebAssemblyにコンパイルし、動的にリンクし、実行できることを意味します。すべてリモートPythonサーバーなしで。こちらでお試しください:JupyterLiteでのNumbaとそのエコシステム。長年の要望WebAssemblyでのNumbaのサポートは長年議論されてきました。Numbaプロジェクトでは2018年にnumba/numba#3284でリクエストが登場し、Pyodideコミュニティはpyodide/pyodide-recipes#192でパッケージングの課題を追跡していました。難しかったのは、NumbaがWebAssembly用にパッケージ化されたことがなかったというだけではありません。Numbaはコンパイラであり、その実行モデルはllvmliteとLLVMに依存します。ネイティブプラットフォームでは、生成されたマシンコードを実行可能なメモリに配置してすぐに呼び出すことができます。ブラウザは、アプリケーションがこのように実行可能なメモリを作成または変更することを意図的に許可しません。したがって、Numbaをブラウザに持ち込むには、ビルドシステムを適応させたり、いくつかのプラットフォームチェックを修正したりする以上のことが必要でした。llvmliteのWebAssembly対応実行エンジン、実行中のブラウザプロセス内でLLVMリンカーを呼び出す方法、および生成されたコードを永続的なPythonランタイムに動的にロードする方法が必要でした。幸いなことに、これは以前にも遭遇した問題でした。インタラクティブC++からllvmliteへ以前のXeus-Cpp on JupyterLiteでの作業により、Clang-Repl C++インタプリタがブラウザに導入されました。Clang-ReplもWebAssembly下でLLVMの従来のJITメカニズムを使用できないため、異なるパイプラインを持つWebAssembly実行モデルを導入しました。生成されたLLVM IRをWebAssemblyオブジェクトファイルにコンパイルします。それをwasm-ldでWebAssemblyサイドモジュール(動的にロードされる共有ライブラリのWebAssembly相当)にリンクします。サイドモジュールを実行中のアプリケーションに動的にロードします。そのシンボルを解決し、コンパイルされた関数を呼び出します。新しい入力は、実行中のプログラムを段階的に拡張します。新しくロードされたサイドモジュールは、メインアプリケーションとメモリを共有し、そのエクスポートされたシンボルは後でロードされたモジュールで使用できます。この作業は、現在ブラウザでのXeus-Cppの基盤となっています。また、インタラクティブC++ワークフローに関するFOSDEM 2026での発表の主題でもあります。このプロジェクトの背後にある中心的な認識は、同様のアーキテクチャをllvmliteに適用できるということでした。llvmliteを通じて生成されたLLVMモジュールを取得し、WebAssemblyオブジェクトをエクスポートし、LLVMのリンカーであるLLDをインプロセス再入可能ドライバ経由で呼び出し、各結果をEmscriptenサイドモジュールとしてロードするWebAssembly実行エンジンを実装しました。インプロセスでLLDを使用することが不可欠です。wasm-ldサブプロセスを起動することは、ブラウザ内ではオプションではありません。モジュールはグローバルにロードされ、維持されるため、ランタイムライブラリ、コンパイラ生成ヘルパー、およびユーザー関数がお互いのシンボルを解決できます。これにより、llvmliteは、ブラウザのセキュリティモデルを尊重しながら、Numbaが必要とする増分実行動作を実現します。さらに進む前に、このエンジンを直接使用してLLVM IRをコンパイル、最適化、検査、および実行しました。また、インプロセスでのGraphvizレンダリングを有効にし、dot実行可能ファイルをサブプロセスとして起動せずに制御フローグラフを表示できるようにしました。この低レベルのパイプラインはこちらで探索できます:JupyterLiteでのllvmliteとGraphviz。ブラウザ内で完全にLLVM IRを構築し、その制御フローグラフをレンダリングします。Numbaへのスタックの歩みNumbaは、はるかに親しみやすいレベルから始まります。ユーザーは通常のPython関数を書き、@jitまたは@njitを適用します。from numba import njit@njitdef add(a, b): return a + badd(1.0, 2.5)この小さな例の背後には、完全なコンパイラパイプラインがあります。NumbaはPythonバイトコードを読み取り、独自の内部表現を構築します。引数と中間値の具体的な型を推論し、コンパイラ変換を実行し、llvmliteを通じて型付けされたプログラムをLLVM IRに低下させます。ネイティブマシンでは、llvmliteは結果をLLVMのJIT実行エンジンに渡します。JupyterLiteでは、代わりに新しいWebAssemblyエンジンにモジュールを渡します。モジュールはコンパイルされ、サイドモジュールとしてリンクされ、実行中のXeus-Pythonカーネルにロードされ、Numbaがそれを呼び出せるようにWebAssembly関数テーブルを通じて公開されます。単一のユーザー関数は、複数のモジュールを含む場合があります。Numbaは最初にNumba Runtime(NRT)などのランタイムサポートをロードし、次にコンパイラ生成ヘルパー、そして最後にユーザー関数とそのCPython呼び出し可能ラッパーを含むモジュールをロードする場合があります。これらのモジュールは、後続のコードが前のモジュールによって提供されるシンボルを解決できるように、正しい順序でロードする必要があります。また、@njit(cache=True)を通じて永続的なオブジェクトキャッシュも追加しました。キャッシュが有効な関数がブラウザセッションで再度使用されると、NumbaはJupyterLiteの永続ファイルシステムからキャッシュされたコンパイルデータを復元します。llvmliteは対応するWebAssemblyオブジェクトを再利用し、新しいカーネルプロセスに新しいサイドモジュールを再リンクしてロードします。結果は、ブラウザでの本物のNumbaコンパイルと実行です。Numbaを模倣するインタプリタでも、ノートブックの背後に隠されたリモートサービスでもありません。emscripten-forgeでのNumbaのパッケージングNumbaがブラウザで動作するようになると、採用の鍵はソフトウェアを配布できるかどうかです。ここでemscripten-forgeが登場します。emscripten-forgeは、WebAssembly用のブラウザ内ソフトウェア配布システムです。conda/mambaスタックとconda-forgeを基盤としており、Webブラウザ向けの完全なパッケージ管理ソリューションと、Python科学スタック(NumPy、SciPy、LLVM、Clang、LLD)、Rスタックだけでなく、ネイティブコマンドラインアプリケーションや完全なツールチェーンを含む広範なパッケージコレクションを提供します。Numbaは現在emscripten-forgeで利用可能であり、JupyterLiteデプロイメントで簡単に使用できるようになりました。JupyterLiteターミナルでMambaを使用してWebAssemblyビルドのNumbaを動的にインストールします。Numba上のエコシステムを解き放つスカラー加算をコンパイルすることは重要なマイルストーンですが、より広範な動機は、Numbaが利用可能になった後に可能になるエコシステムです。私たちのデモンストレーションは、認識可能なNumbaの例と数値配列カーネルから始まります。次に、Numbaを直接、または独自のコンパイルバックエンドを介して使用するパッケージを上に歩きます。PyTensorは、Numbaリンカーを使用してシンボリック数値グラフをコンパイルできます。PyTensor上に構築されたPyMCは、同じブラウザ環境に馴染みのある確率モデルをもたらします。interpolation.pyは、数値経済学で使用されるNumbaアクセラレーテッド補間ルーチンを提供します。Dolo.pyは、このスタックを基盤として、経済学者向けのJIT対応数値ルーチン(確率過程、意思決定ルール)と、動的プログラミングの最適化された解法を提供します。