🚀

Blazor WebAssemblyのAOTコンパイルでクライアントサイドの画像処理を高速化

5 min read

Blazor WebAssemblyで画像処理

Blazor WebAssemblyを利用することによってC#のコードをWebブラウザ上で実行することが可能になりました。


ASP.NET Core Blazor の概要 (Microsoft) より

これまでサーバサイドで実行していた処理をクライアントサイドで実行することで、サーバの運用コストの低減につながりそうです。私の場合は画像処理をするアプリを開発していて、Blazor WebAssemblyによってクライアントサイドで画像処理をしてみたいと考えていました。

クライアントサイドでImageSharpを使った画像処理(2020年4月)

C#の画像処理ライブラリのImageSharpを利用して、クライアントサイドで画像処理をしたコードがRedditに投稿されました。

https://www.reddit.com/r/Blazor/comments/g932ks/image_edit_with_blazor_client_and_imagesharp/

https://aniruddhaachar.github.io/Blazor-ImageEdit/

WebAssemblyの可能性を感じさせるデモです。しかし、Redditのスレッドでは処理速度の遅さも指摘されています。

Redditに投稿されたコードをシンプルにして、Webブラウザから指定した画像ファイルの横幅をImageSharpで取得するコードは以下のようになります。指定する画像ファイルのサイズにもよりますが、処理速度が遅いことがわかると思います。

Pages/Index.razor
@page "/"

@using SixLabors.ImageSharp;
@using SixLabors.ImageSharp.Formats;
@implements IDisposable

<div class="col-md">
    <div class="row">
        <h3>Upload an image</h3>
    </div>
    <div class="row">
        <InputFile OnChange="@HandleFileSelected" />
    </div>
    <div class="row">
        @width
    </div>
</div>
<div class="row">
</div>

@code {
    private List<IBrowserFile> loadedFiles = new();
    private long maxFileSize = 1024 * 1024 * 15 ;
    private int maxAllowedFiles = 10;

    private double width = 0;

    private async Task HandleFileSelected(InputFileChangeEventArgs e)
    {
        loadedFiles.Clear();

        foreach (var file in e.GetMultipleFiles(maxAllowedFiles))
        {
            try
            {
                loadedFiles.Add(file);

                var ms = new MemoryStream();
                await file.OpenReadStream(maxFileSize).CopyToAsync(ms);

                IImageFormat imageFormat;
                Image imgSharp = Image.Load(ms.ToArray(), out imageFormat);

                width = imgSharp.Width;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
        }   
    }

    public void Dispose()
    {
    }
}

実際に私が開発しているアプリをBlazor WebAssemblyでも実行させてみたのですが、簡単な画像処理に数十秒かかり、現実的な速度ではありませんでした。

ImageSharpの処理速度に関する見解(2021年2月)

WebAssemblyでのImageSharpの処理速度については以下のスレッドで議論がありました。

https://github.com/SixLabors/ImageSharp/discussions/1544#discussioncomment-378028

今後、この問題や同様の問題にぶつかった人のために、回答を掲載します。

パフォーマンスが重要な場合、ImageSharpをブラウザで使用することはお勧めしません。
Update: ツイッターでチャットをしたり、自分で読んでみたところ、SIMDの問題ではないことがわかりました。クライアントサイドのBlazorはいまだにインタプリタを使用しており、ブラウザで計算量の多いタスクを行うことは実質的に不可能です。.NET 6ではAOT(dotnet/aspnetcore#5466)が導入されるはずで、mono-wasm + AOTでジェネリックがどのように実装されるかによって、状況が改善されるかもしれません。

WASMはまだSIMDをサポートしていないので、Blazorでライブラリを高速化するのは不可能です。
WASMにSIMD intrinsicsを追加するという提案 (https://github.com/WebAssembly/simd) がありますが、.NETでランタイムサポートされるまで何年もかかると思います。

この時点ではBlazor WebAssemblyでImageSharpを利用することは現実的ではなく、AOTによって問題が解決する可能性があるけれども、まだよくわからないという状況でした。

.NET 6 Preview 4でAOTコンパイルのサポート(2021年3月)

.NET 6 Preview 4でAOTコンパイルがサポートされ、処理速度の問題を解決できる可能性が出てきました。

https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-4/

画像処理が5倍高速化した例も示されました。

セットアップ

Blazor WebAssemblyとImageSharpで開発した画像処理のアプリをAOTコンパイルして処理時間を比較します。

ワークロードのインストール

.NET 6 Preview 7より以前ではmicrosoft-net-sdk-blazorwebassembly-aotというワークロードを使用していました。もしもmicrosoft-net-sdk-blazorwebassembly-aotがインストール済みであれば、以下のコマンドでアンインストールします。

> dotnet workload uninstall microsoft-net-sdk-blazorwebassembly-aot

.NET 6 Preview 7以降の環境で以下のコマンドを実行してワークロードをインストールします。

> dotnet workload install wasm-tools

https://devblogs.microsoft.com/aspnet/asp-net-core-updates-in-net-6-preview-7/

RunAOTCompilationの追加

ProjectName.csprojRunAOTCompilationtrueにした設定を追加します。

ProjectName.csproj
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
    <PropertyGroup>
        <TargetFramework>net6.0</TargetFramework>
        <Nullable>enable</Nullable>
        <RunAOTCompilation>true</RunAOTCompilation>
    </PropertyGroup>
</Project>

実行

コンパイル

以下のコマンドでコンパイルします。コンパイルには10分以上かかりました。

> dotnet publish -c Release

処理時間の比較

5個の画像ファイルを処理した際の実行時間は以下のようになりました。このアプリではWebブラウザから画像を指定してImageSharpで処理するのですが、通常のビルドではImageSharpのImage.load()に時間がかかっているようでした。AOTコンパイルすることで処理速度が約70倍高速になり、これなら十分に実用的な時間で処理できそうです。

ビルド方法 処理時間
通常のビルド (Debug) 99.192 秒
通常のビルド (Release) 99.165 秒
AOTコンパイル 1.406 秒

まとめ

Blazor WebAssemblyのAOTコンパイルでクライアントサイドの画像処理を高速化することができました。.NET 6はまだプレビュー段階ですが、AOTコンパイルのサポートなど活発に開発が進められていて、今後も期待しています。

Discussion

ログインするとコメントできます