dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.35k stars 9.99k forks source link

[Blazor] wasm - Javascript interop when returning array from wasm is very slow #14698

Closed aclarkequad closed 4 years ago

aclarkequad commented 5 years ago

Migrating from a Silverlight image processing app to Blazor-wasm, we're experiencing poor performance returning a uint array of the pixel data from wasm -> js.

This simple code takes 16+ seconds to simply return from wasm before executing the javascript function to fill the canvas with a solid colour:

Partial Razor Page code

<div>
    <canvas id="renderCanvas" width="800" height="600"></canvas>
</div>

@code {
    async void LoadImage() {
        var len = 800 * 600 * 4;
        var buff = new uint[len];
        for (int i = 0; i < len; i += 4) {
            buff[i] = 255;
            buff[i + 3] = 255;
        }
        await JsRuntime.InvokeAsync<object>("FindCanvas", TimeSpan.FromSeconds(60), buff);
    }
}

JavaScript code

window.FindCanvas = (bufferData) => {
    var c = document.getElementById("renderCanvas");
    ctx = c.getContext("2d");
    var ui = Uint8ClampedArray.from(bufferData);
    var imgData = ctx.createImageData(800, 600);

    for (let i = 0; i < imgData.data.length; i++) {
        imgData.data[i] = ui[i];
    }

    ctx.putImageData(imgData, 0, 0);
}

Running this on any browser on my PC takes ~16 seconds to even get to the JavaScript which executes quickly as expected. The profiling I could do showed the delay was in a promise in wasm.js, but this is not my area of expertise so any input will be welcome.

Is this an issue inherent with wasm and not Blazor client specific, mono-wasm issue that will be improved in the future, or am I doing something fundamentally wrong?

mkArtakMSFT commented 5 years ago

Thanks for contacting us, @aclarkequad. We'll look into this after we're done with the upcoming 3.1 release.

Daddoon commented 5 years ago

Seem to be related to the serialization. Calling:

JsonSerializer.Serialize(buff);

Does the same effect.

Testing with Newtonsoft.Json:

string result = JsonConvert.SerializeObject(buff);

Do the same effect. Testing from a pur Javascript is indeed fast, even serialization.

When testing serialization this appear in the console:

WASM: GC_MAJOR_SWEEP: major size: 992K in use: 26959K
blazor.webassembly.js:1 WASM: GC_MAJOR: (LOS overflow) time 16.52ms, stw 16.56ms los size: 1024K in use: 24K
blazor.webassembly.js:1 WASM: GC_MINOR: (LOS overflow) time 0.50ms, stw 0.55ms promoted 0K major size: 992K in use: 180K los size: 1024K in use: 24K

I may be wrong but i think there is some kind of operations going on the large heap during serialization that decrease the performances greatly.

I think this is related to mono-wasm performance. Disclaimer: I'm not working for Microsoft

GeorgeS2019 commented 4 years ago

This very slow JSInterop issue is very relevant to Blazor Canvas2D and WebGl 3D drawing. One option is to use Skia CanvasKit in WebAssembly that has been made accessible through UNO to C#

SteveSandersonMS commented 4 years ago

Serializing large blocks of data will be slow. If you need a faster way of transferring the data, look into lower-level unmarshalled interop techniques. For example, https://github.com/SteveSandersonMS/BlazorInputFile/blob/master/BlazorInputFile/wwwroot/inputfile.js#L50. Beware that this is not a technique we currently support, so it's possible there will be breaking changes to these internal APIs later.