Web開発
ブラウザでのNumPyの高速化
Faster NumPy in the Browser (notebook.link)
要約
Emscripten-forgeのNumPyパッケージがOpenBLASをWebAssemblyにリンクするようになり、ブラウザ上でのNumPyのパフォーマンスが大幅に向上しました。特にnp.matmulのような行列演算では、以前の単純なループ実装と比較して、float32で約30.92倍、float64で約14.90倍の高速化が達成されています。これは、Emscripten-forgeが言語非依存のWebAssembly向けソフトウェア配布プラットフォームとして機能していることを示しています。
全文翻訳
長らく、ブラウザでNumPyを実行することは、アクセラレーテッドBLASなしで実行することを意味していました。行列乗算は単純なループ(ポータブルですが、キャッシュやSIMDを無視)にフォールバックしていました。それが変わりました。
Emscripten-forgeのNumPyパッケージは現在、WebAssemblyにOpenBLASをリンクしています。n = 1024の正方行列の場合、np.matmulは(float32で)約30.92倍、(float64で)約14.90倍高速になります。
QuantStackによって提供されたカーネルを含む、OpenBLASの次のリリース(Emscripten-forgeの実験パッケージとして既に利用可能)は、さらにそれを押し進め、オプションのRelaxed SIMDビルドは、それをサポートするエンジンでさらに一歩進みます。
Emscripten-forgeとは何ですか?
Emscripten-forgeは、WebAssembly向けのソフトウェア配布プラットフォームです。conda-forgeと共に、WebAssembly向けにcondaエコシステムを再構築しています。そのため、コンパイラ、ランタイム、共有ライブラリは、一貫したABIを持つ再配布可能なcondaパッケージとして提供されます(Python wheelsやRパッケージではなく、どの言語エコシステムでもリンクできるネイティブライブラリです)。
科学言語をブラウザに持ち込むことは十分に困難であり、これまで、それはほとんど一度に1つの言語で行われてきました。PyodideはPythonでそれを先駆しました。その後、WebRは同じ前提でRのためにそれを実行しました。それぞれが自己完結型の言語固有の配布です。
Emscripten-forgeは異なる形状をとります。言語に依存しない配布です。PyodideとWebRプロジェクトの基盤となる作業の上に構築されており、PythonとRだけでなく、C++、OCaml、Lua、コンパイラツールチェーン、およびその他の多くのツール(パッケージマネージャーを搭載)もカバーしています。
欠けていたコンパイラ:wasm32上のFortran
Fortranは、LAPACK、歴史的にSciPyの大部分(SciPyがFortranフリーになるよう努力しているため変化していますが)、およびRのコンパイル済み統計ルーチンなどの基盤となる科学パッケージの中核をなしています。それらのいずれかをWebAssemblyに持ち込むことは、wasm32をターゲットにできるFortranコンパイラを持ち込むことを意味します。
しばらくの間、Pyodideはそのようなコンパイラの不在を回避していました。そのSciPyビルドはf2c(FortranからCへのトランスレータ)で生成されたため、FortranソースはCにトランスパイルされ、既存のEmscriptenツールチェーンでコンパイルされました。その回避策によりSciPyはブラウザで実行できるようになりましたが、Rの配布では実際のFortranコンパイラを回避できなかったため、それは十分ではありませんでした。その制約が、Emscripten-forge上でRをブラウザに持ち込むという文脈で、WebAssembly用のFlang(LLVMのFortranフロントエンド)の作業を開始した理由です。
WebAssemblyでのOpenBLAS
OpenBLASは、広く使用されている最適化されたBLAS(Basic Linear Algebra Subprograms)の実装です。BLASは3つのレベルに組織されています。レベル1はベクトル-ベクトル演算(例: AXPYおよびドット積)をカバーします。レベル2は行列-ベクトル演算(例: GEMV、A @ xなど)をカバーします。レベル3は行列-行列演算(例: GEMM、np.matmulなど)をカバーし、大規模な密線形代数を支配します。
OpenBLASはLAPACK(Linear Algebra PACKage)も提供しています。線形システム、因数分解、固有値問題の解決のための高レベルルーチンであり、ほとんどのnp.linalg呼び出しは最終的にこれらのLAPACKエントリポイントに委譲されます。
WebAssembly用のFortranツールチェーンの構築自体が、複数人による努力でした。Isabel ParedesとSerge Gueltonは、George StaggのWebAssemblyでのFortranに関する作業に触発されて、WebAssembly用の最初のFlangパッチを作成しました。Serge Gueltonはまた、その作業の一部をLLVMにアップストリームしました。Isabel Paredesは現在、Emscripten-forge上の最新のレシピ、flang_emscripten-wasm32をメンテナンスしています。
しかし、動作するFlangがあっても、ツールチェーンだけではOpenBLASをブラウザに持ち込むには十分ではありませんでした。最初のOpenBLASおよびLAPACKビルドはすぐに使用できませんでした。WebAssemblyはネイティブターゲットよりも厳格な呼び出し規約を強制するため、Emscripten下でのライブラリのコンパイル、アーカイブ、リンクを可能にするために追加のパッチが必要でした。Ian Thomasは、Emscripten-forgeのOpenBLASレシピ(アップストリームOpenBLASと、この記事で測定されたNumPyビルド間のブリッジ)にあるパッチを使用して、OpenBLASをFlangとEmscriptenに適応させました。
この記事の残りの部分は、最後のマイルを歩みます。NumPyがBLASを呼び込めるようになったときに何が変わったのか、そしてOpenBLAS 0.3.34と今後の0.3.35が何を提供するのかを説明します。
最後のマイル:NumPyがOpenBLASをリンク
最後のステップは、NumPyをアクセラレーテッドBLASにリンクすることでした。Emscripten-forgeの以前のNumPyパッケージ(emscripten-forge-4xの2.5.2)はOpenBLASなしで出荷されていたため、np.matmul、@、およびnp.linalgはポータブルCループ(キャッシュ階層やSIMDを無視した単純な実装)にフォールバックしていました。行列積は、算術よりもメモリトラフィックにほとんどの時間を費やしていました。これが、BLASなしのベースラインです。
emscripten-forge/recipes#6310以降、emscripten-forge-4x(2.5.3、ビルド3)のデフォルトNumPyは、WASM SIMD(TARGET=WASM128_GENERIC)を備えたOpenBLAS 0.3.34をリンクしています。これは現在、安定したメインチャネルリリースです。np.matmul、@、およびnp.linalg.solveへの呼び出しは、WebAssemblyのアクセラレーテッドBLASにディスパッチされるようになりました。
パッケージモデルがこれを強力にしています。Emscripten-forgeは、実質的にWebAssembly向けのconda-forgeです。パッケージはレシピからビルドされ、condaチャネルに公開され、共有ABIの下でパッケージマネージャーを使用してインストールされます。これは、linux-64でのワークフローと同じですが、emscripten-wasm32をターゲットにしています。そのモデルでは、OpenBLASは個別のcondaパッケージです。NumPyは実行時にlibopenblasを動的にリンクするため、NumPyを再ビルドせずにOpenBLASをアップグレードできます。これはエコシステム全体に利益をもたらします。SciPyやscikit-learnなどの科学Pythonプロジェクト、およびxtensor-blasなどの非Pythonスタックは、すべて同じライブラリを共有します。対照的に、PyPI NumPyホイールは、ビルド時にBLASのスナップショットをバンドルします。
同じNumPy 2.5.3パッケージは、OpenBLAS 0.3.35が公開されると、自動的にそれを使用するようになります。
OpenBLAS 0.3.34対BLASなし
BLAS実装なしの同じEmscripten-forgeスタックと比較して、n = 1024の正方np.matmulは、約30.92倍(float32)および14.90倍(float64)(0.90 / 0.98 GFLOPSに対して28.0 / 14.6 GFLOPS)に達します。完全なサイズのグリッドとシングルスレッドのlinux-64参照は付録Fにあります。この比較に焦点を当てたグラフと表は付録Bにあります。
両方のdtypeとサイズグリッドの幾何平均(分母はOpenBLAS 0.3.34の時間)。行列積が最も恩恵を受けます。
一部のnp.linalg.* APIでは、それらはLAPACKに委譲されるため、BLASなしと比較してゲインが小さく(1.08–1.65倍)、OpenBLASでのその実装はまだWebAssembly用に最適化または特化されていません。A @ xとベクトルnp.dotは1倍近くのままです。OpenBLAS 0.3.34は、そのレイアウトに対して高速な列優先GEMVを提供しません。OpenBLAS 0.3.35はそのカーネルを追加します。
BLASなしでは、np.matmulのスループットはnとともに減少します(単純なGEMM、キャッシュミス)。OpenBLAS 0.3.34では、nとともに増加します。GEMM(@、2次元np.dot、np.tensordot、np.linalg.multi_dot)の上に実装された操作は、同じ傾向に従います。
nと関連数値に対する速度向上は付録Bにあります。
n = 1024(float32)のnp.linalgでは、中央値の時間はBLASなしと比較して約1.2〜2.1倍減少します(solve 2.11倍、cholesky 1.88倍、qr 2.03倍、inv 1.66倍、eigh 1.60倍、svd 1.21倍。float64は数パーセント以内で一致します)。
行列-ベクトル積では、x @ Aは既に0.3.34でレベル2カーネルを使用しています(float32、n = 1024で14.86倍)。一方、A @ xはBLASなしの約4 GFLOPS(1.02倍)近くにとどまります。
OpenBLAS 0.3.35はさらに高速になります
次のOpenBLASリリースには、既に開発中のWASM SIMD作業が含まれています。以下の数値は、emscripten-forge/recipes#6761(0.3.35.dev0、develop @ 539bb47、まだTARGET=WASM128_GENERIC)によって公開されたopenblas-experimentalパッケージを使用しています。ポータブルsimd128_hbf81ecf_3(デフォルト)とオプトインのrelaxed_simd_h13a9a80_3です。
WebAssembly Relaxed SIMDは、より高速なベクトル演算(特に融合積和演算)と引き換えに、わずかに緩い浮動小数点セマンティクスを許可するエンジン拡張です。ビルドで有効にされている場合、OpenBLASはそのオペコードを使用できます。
特に記載がない限り、この記事の0.3.35はポータブルSIMD128ビルドを意味します。
QuantStackは、Martin Kroekerによるレビューと統合を経て、WASM SIMDカーネルをOpenBLASにアップストリームしました(Julien Jerphanion、Matthias Meschede)。その作業は、レベル3 GEMM/TRMM、レベル