Edited at

C#で始めるWebAssembly


この記事について

Sansan Advent Calendarの9日目の記事です。11月からSansanにジョインしまして、サーバサイドエンジニアになりました。転職したてではありますが、毎日非常に楽しく働いています。

12/16 追記

@jsakamotoさんのコメントを踏まえて追記しました。Blazorについての理解が深まるコメントをいただきました。本当にありがとうございます!


はじめに

先日、C#でWebAssemblyを動かすことができる技術であるBlazorについて知りました。C、C++、Rust、golangなどでの実績があるのは知っていましたが、C#でも使えることは初耳でした。そこで早速試してみよう!ということです。まずはWebAssemblyから説明をしていきます。


WebAssemblyとは?

クライアントサイドで動作するバイナリフォーマットです。モダンなWebブラウザ*1では、すでにこの形式がサポートされています。wasmと略されることもあります。

WebブラウザはJavaScriptを実行することで、サーバ通信やDOM操作などを行いますが、JavaScriptはスクリプト言語のため、実行速度に限界があります。Webの複雑化にともなって、速度が求められるアプリケーションをブラウザ実行させたいニーズが高まると、言語自体がボトルネックになることが増えてきました。そこでブラウザ上で動作する新しい実行形としてWebAssemblyが登場しました。ただし、JavaScriptの代替ではなく共存することを目的としており、計算や描画などのコアな部分に徐々に利用されつつあるようです。

公式サイトのデモを触ってみると雰囲気がつかめるかと思います。これはゲームなのですが、ブラウザだとは思えないほどヌルヌル動きます。

https://webassembly.org/demo/Tanks/


メリット


  • JavaScriptと比較して、パフォーマンスが良い

  • バイナリ形式なのでサイズが小さくて済む

  • JavaScriptから直接呼び出せるので、部分的にロジック内に導入することが可能

  • サンドボックスなので安全に取り扱うことができる


WebAssembly入門

早速ですが、まずは正攻法的にWebAssemblyを触ってみましょう!

※まずC言語を使って実験しています。C#だけで試したいという方はこの章をスキップしてください。


動作環境


  • Windows10 64bit

  • WSL(Ubuntu16.04)

特に、WSLから操作する必要があるので、先にインストールしておいてください。Ubuntu以外からだとaptコマンドらへんを読み替えて実行してください。Mac、Linuxの方はこちらを参考にしてみてください。


インストール手順

まず、toolchainをインストールするために、Python2.7が必要ですので、インストールしましょう。

$ sudo apt update

$ sudo apt upgrade
$ sudo apt install python2.7 python-pip

Emscripten SDKをインストールします(少し時間がかかります)。

Emscriptenを使うことで、C言語からWebAssemblyにコンパイルできます。

$ git clone https://github.com/juj/emsdk.git

$ cd emsdk
$ ./emsdk install latest
$ ./emsdk activate latest

次に環境変数にPATHを通します。設定のためのシェルが同梱されているので、それをそのまま使いましょう。

$ source ./emsdk_env.sh --build=Release


実行まで

ここまでくれば、あともう少しです!

C言語で「Hello,World」を標準出力させるプログラムを書きましょう。

$ mkdir hello

$ cd hello
$ cat << EOF > hello.c
#include <stdio.h>
int main(int argc, char ** argv) {
printf("Hello, world!\n");
}
EOF
$ emcc hello.c -s WASM=1 -o hello.html

コンパイルが終わるとHTML、JavaScript,wasmファイルが生成されます!非常に簡単ですね。

$ ls

hello.c hello.html hello.js hello.wasm

Emscriptenはデバッグ環境のためのWebサーバ機能があるので、それを使って動作確認しましょう。

$ emrun --no_browser --port 8080 .

起動後に、http://localhost:8080/hello.htmlにアクセスすると....

emscripten.png

ついに、JavaScriptを書かずに、ブラウザにプログラムを埋め込むことができました!

...さて、C#から思いっきり離れてしまいましたが、ここからが本題です。Blazorについて見ていきましょう。


Blazorとは?

Blazorの公式サイトによると、


Full-stack web development with C# and WebAssembly

Blazor runs on WebAssembly, giving you native performance in the browser and a trusted security sandbox.



WebAssemblyとC#を利用したフルスタックWeb開発フレームワークです。

ASP.NETのフレームワークはそのままにWebAssemblyを織り込んだ、というイメージでしょうか。

-C#でコードを書きながらも、WebAssembly上で動作する..!?

-つまり、ASP.NETのスタイルはそのままにWebAssemblyで動作するスゴイな技術です。

語弊があるような表現なので、修正します。

BlazorはWebAssemblyで書かれた.NETのインタプリタ(CLI)がブラウザで動作する技術を指します。

C#の動作するインタプリタをブラウザで再現することによって、C#で書いたコードやライブラリをそのまま再利用できるというメリットがあります。

そのため、直接WebAssemblyが...という技術ではなく、WebAssemblyを応用してC#を動かそうと試みる技術ですね。


Blazorを使ってみる

物は試しです。早速、Blazorをインストールして使ってみましょう。


動作必須環境


  • .NET Core 2.1 SDK (2.1.500 以上)

  • Visual Studio 2017 (15.9 以上) with the ASP.NET and web development workload selected.


インストール

MarketPlaceよりASP.NET Core Blazor Language Servicesをダウンロードします。

blazor.PNG

ダウンロードしたファイルを起動すると、インストーラが起動します。

install.PNG

インストールをクリックしてしばらく待つと...

done.PNG

完了です。これでBlazorを使う準備ができました。


デモアプリの実行

それでは、公式からデモアプリを取ってきて実行しましょう。

※Visual Studioから起動してもらってもかまいません。

$ dotnet new -i Microsoft.AspNetCore.Blazor.Templates

$ dotnet new blazor -o BlazorApp1
$ cd BlazorApp1
$ dotnet run

これでWebAssemblyが使える状態にあります。

https://localhost:5001にアクセスすると以下のような画面が表示されるとOKです!

new.PNG


フィボナッチ数列でも計算してみる

では、このプロジェクトを少しだけ改造してみましょう。

処理時間のかかりそうなプログラムを作るため、n=30のフィボナッチ数列を計算するプログラムを書いてみます。Pages/Counter.chtmlを以下のように変更しました。


Counter.chtml

@page "/counter"

<h1>Fibonacci Calculation</h1>

<button class="btn btn-primary" onclick="@DoFibonacci">Calculate!</button>
<p>Result : @value</p>

@functions {
int value = 0;
const int target = 30;

void DoFibonacci(){
value = Calc(target);
}

int Calc(int i){
if (i == 0 || i == 1)
return 1;
return Calc(i-1) + Calc(i-2);
}
}


デモアプリをそのまま上書きしてしまったので、Counter.chtmlのままですが、中身をフィボナッチ数列の計算ロジックに変更しました。それでは実行してみます。

$ dotnet run

blazor-fib30.gif

........おそい!!!!

JavaScriptで実行するよりも遅いんじゃないだろうか、と思ってローカル環境で同様の関数をJavaScriptで書いて実行してみたところ、1秒もかからず終了しました(実行環境がフェアではないにしても)。

まさかの結果となりましたが、使ってみることで以下のようなデメリットも多く見えてきました。


  • GIF画像でもわかるように動作がもっさりしている

  • DLLなどを取ってくる場合にオーバヘッドがかなり大きい

  • scriptタグをindex.html以外に記載することができなかった(!)

  • Visual Studioで一度開くとbin/ディレクトリ下に、DebugReleaseの両方が作成されてしまうことによってdotnet runが実行できなくなるバグがあった*2

速度やサイズの問題はWebAssemblyの長所を打ち消してしまうもので、ブラウザ上でILを動かそうとする難しさが感じられますね...。それでもJavaScriptを書かずにWebAssemblyが実行できるBlazorには大きな可能性を感じました。起動までは簡単でしたからね。

パフォーマンスについては、WebAssemblyで書かれたとはいっても、インタプリタ形式でc#を実行することがボトルネックになっていると推測されます。


Blazorを利用するメリット

触ったことによるデメリットが見つかった反面、以下のようなメリットも挙げられます。


  • C#/.NETのコードやライブラリ(dll)などをそのままブラウザ上で再利用可能になる

  • 起動までに複雑なステップを踏む必要がなく、開発をすすめることができる

  • Visual Studioで開発できる!(C#エンジニアにとってはとても重要)

パフォーマンス面も含めて、今後の拡張にはとても期待できますね。


まとめ

WebAssemblyを触ってみて、改めて非常に興味深い技術だと思いました。Web界隈のアセンブリ職人が誕生する日も遠くのではないでしょうか。せっかくなのでフェアな環境を用意してパフォーマンス検証までしたかったですね。。。

*1 つまり、IEは除く

*2 https://github.com/aspnet/Blazor/issues/261


参考

https://webassembly.org/

https://webassembly.github.io/spec/core/index.html

https://blazor.net/

https://qiita.com/jsakamoto/items/20d4893f6c8cdb0356f6

http://blog.stevensanderson.com/2018/02/06/blazor-intro/