.NETの新しいWeb技術であるBlazor、大きくBlazor ServerとBlazor WebAssemblyの2種類の動作形態があることは皆さんご存知(?)ですね。そして.NET 8では、これにさらに様々な新機能も追加されてきています。
今回はこのうちBlazor WebAssemblyの実行パフォーマンスについて検証してみます。
WebAssemblyは速いだろうと思っていたな?
まず最初に、WebAssemblyは下記Wikipediaの解説にもあるとおり、ネイティブ(バイナリ)相当のコードが実行されるため速い!というイメージがありますね。
ネイティブコード相当の高速性・隔離環境でのメモリ安全な実行による安全性・仮想マシンによるハードウェア/プラットフォーム可搬性・ソースプログラミング言語中立性などを特徴とする。
それを期待してベンチマークをとってみたのが、2023年に登壇したBuriKaigiでのことでした。
下記のような、モンテカルロ法で円周率を近似計算するプログラムを走らせてみて、
private static double MontePi(int n)
{
var rand = Random.Shared;
var count = 0;
if (n < 1) return 0;
for (int i = 0; i < n; i++)
{
var x = rand.NextDouble();
var y = rand.NextDouble();
if (x * x + y * y <= 1)
{
count++;
}
}
return 4.0 * count / n;
}
結果、こちらのようにJavaScriptの約20倍遅いという、惨敗といえるベンチを叩き出していました。
モンテカルロ法 円周率近似計算
50,000,000 サンプル
Blazor WebAssembly
9586 ms
9594 ms
9500 ms
JavaScript
521 ms
536 ms
537 ms
1年を経て逆転!?
そして翌2024年のBuriKaigiで、このようなツイートを目にします。
昨日はたくさんのセッションと美味しい鰤をどうもありがとうございました!
— @jsakamoto (@jsakamoto) January 21, 2024
一点余談ですが Blazor WebAssembly は遅いです。理由は MSIL をインタープリタで実行するからです。インタープリタが Wasm 上で走ってます。AOT すると改善されます。Wasm自体はそれなりに速いです。#burikaigi_h #burikaigi pic.twitter.com/Fq6P3OMhGp
Blazor WebAssemblyのAOTコンパイルによる高速化とAzure Static Web Appsへのデプロイ の記事を参考に、ベンチマークしていたプロジェクトを再ビルドします。
具体的には、下記2つの手順で完了です。
- VisualStudioを終了させた状態で、コマンドラインから
wasm-tools
ワークロードをインストールする
> dotnet workload install wasm-tools
- VisualStudioを起動し、Blazor WebAssemblyプロジェクトの
ビルド > 全般 > 公開時にAhead Of Time(AOT)コンパイルを使用する
にチェックを入れる
これで再度ベンチマークしてみた結果がこちら。
それでもJavaScriptには及ばないとはいえ、AOTなしに比べて圧倒的なパフォーマンスを得ることができました!
モンテカルロ法 円周率近似計算
50,000,000 サンプル
WebAssembly AOTなし
9586 ms
9594 ms
9500 ms
WebAssembly AOTあり
1112 ms
1047 ms
1034 ms
JavaScript
521 ms
536 ms
537 ms
とはいえ
このように、AOTを有効にすることで圧倒的パフォーマンスを得ることには成功しましたが、.razorページに当てていたcssが一部無効になっていたり、少なくともReflectionが使えなくなるはずだったり、ノールックで採用できるものではなさそう、ということも分かってきました。
あと、バイナリサイズも大きくなるようなので、ページの初回ロードでユーザーを待たせるというデメリットもありますね。
これらデメリットを補って余りあるパフォーマンスであるとも考えられるので、状況さえ許せば積極的に採用していきたいものではありますね!