🙆♀️
C# WebAssembly AOT vs Chrome Javascriptでベンチマークしてみた
C# .NET WebAssembly vs Chrome Javascript
.NET7になりBlazorも正式にAOTコンパイルができるようになりました.
今後は.NET WebAssembly without Blazor UIにある通り,Blazorなしの純粋なC#でもWebAssemblyがサポートされます.
ますます.NETのWebAssemblyが進化していきますね.
AOTコンパイルすることでJSとどの程度パフォーマンスに差があるのか,WebAssemblyでC#や.NETを採用するメリットがあるのか検証してみました.
前提条件
厳密なベンチマークではなく簡単なものです.
また,コードの最適化は行ってません.
C#のものをできるだけJSで再現してますが,
JS側でUint8Arrayではなくただの2次元配列を利用していたり,
まだまだ最適化できる部分はあると思いますが,
大まかなベンチマークなので目安だと思ってください.
アルゴリズムは,
以前書いた記事と同じマンデルブロー集合を生成するコードを利用します.
結果
検証マシン
- Memory DDR4 32GB 2666Mhz
- CPU Core i7 12700
- Chromium 108
Platform | 実行速度 |
---|---|
Javascript | 116ms |
.NET Blazor AOT | 68ms |
.NET 7 Blazor | 2226ms |
.NET 7 | 44ms |
結論
今回の計測では意外にもBlazor AOTのほうがJavascriptよりも高速でした.
今後はパフォーマンス目的でC#や.NETを採用するメリットになりそうですね!
計測に利用したコード
今回はいろいろごにょごにょしたので,
計測部分のみ載せておきます.
C#
record struct Size(int Width, int Height);
class Program
public static void Run() {
var size = new Size(1000, 1000);
var buffer = new byte[size.Width, size.Height];
var sw = new Stopwatch();
sw.Start();
_ = GenerateMandelbrot(buffer, size, -2.0, -1.6, 1.0, 1.6, 100);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
static byte[,] GenerateMandelbrot(
byte[,] buffer,
Size size,
double xMin,
double yMin,
double xMax,
double yMax,
int iterLimit
) {
for (int py = 0; py < size.Height; py++) {
for (int px = 0; px < size.Width; px++) {
// get coordinates of pixel (px, py)
double x = xMin + ((xMax - xMin) * px) / (size.Width - 1);
double y = yMin + ((yMax - yMin) * py) / (size.Height - 1);
double a = x, b = y;
int iter = 0;
do {
// use tuples for iteration
(a, b) = (a * a - b * b + x, 2 * a * b + y);
iter++;
} while (iter <= iterLimit && a * a + b * b < 4);
if (iter > iterLimit) {
// interior color
buffer[py, px] = 0;
}
else {
// exterior color banded by iteration
byte color = (byte)(iter % 2 == 0 ? 1 : 2);
buffer[py, px] = color;
}
}
}
return buffer;
}
}
}
Javascript
const size = { width: 1000, height: 1000 };
const buffer = (new Array(1000)).fill((new Array(1000)).fill(0));
const startTime = Date.now();
GenerateMandelbrot(buffer, size, -2.0, -1.6, 1.0, 1.6, 100);
const endTime = Date.now();
console.log(endTime - startTime);
function GenerateMandelbrot(
buffer,
size,
xMin,
yMin,
xMax,
yMax,
iterLimit
) {
for (let py = 0; py < size.height; py++) {
for (let px = 0; px < size.width; px++) {
// get coordinates of pixel (px, py)
const x = xMin + ((xMax - xMin) * px) / (size.width - 1);
const y = yMin + ((yMax - yMin) * py) / (size.height - 1);
let a = x, b = y;
let iter = 0;
do {
// use tuples for iteration
a = a * a - b * b + x
b = 2 * a * b + y;
iter++;
} while (iter <= iterLimit && a * a + b * b < 4);
if (iter > iterLimit) {
// interior color
buffer[py, px] = 0;
}
else {
// exterior color banded by iteration
const color = (iter % 2 == 0 ? 1 : 2);
buffer[py, px] = color;
}
}
}
return buffer;
}
Discussion