【问题标题】:Blazor wasm invoke javascript, pass large array is very slowBlazor wasm 调用 javascript,传递大数组很慢
【发布时间】:2021-01-02 05:59:03
【问题描述】:

我有一个 blazor wasm 应用程序。我正在调用一个接收双精度数组的 javascript 函数。 这非常慢,尤其是当数组很大时。

测试见下面的代码:

javascript(“test.js”):

function testSumArray(array) {
    var t0 = performance.now();
    sumArray(array);
    var t1 = performance.now();
    console.log('From JS, time to sum: ' + (t1 - t0) / 1000 + ' s');
}

function sumArray(array) {
    var i;
    var s = 0;
    for (i = 0; i < array.length; i++) {
        s += array[i];
    }
    return s;
}

以及c#代码(index.razor):

@page "/"
@inject IJSRuntime JSRuntime;

@using System.Text
@using BlazorWasmOnlyTest.Shared
<h1>Hello, world!</h1>

Welcome to your new app.

<div class="container">
    <div class="row mb-2">
        <div class="col">
            <button class="btn btn-primary" @onclick="@TestInvokeJS">Test invoke js</button>
        </div>
    </div>
</div>

@code {
    private int _id;
    private string _status = "";
    private DataInputFileForm _dataInputFileForm;

    private async void TestInvokeJS()
    {
        var n = 100000;
        var array = new double[n];
        for (int i = 0; i < n; i++)
        {
            array[i] = i;
        }
        var w = new System.Diagnostics.Stopwatch();
        w.Start();
        await JSRuntime.InvokeVoidAsync("testSumArray",array);
        w.Stop();
        Console.WriteLine($"C# time to invoke js and sum: {w.ElapsedMilliseconds/1000:F3} s");
    }
}

为了完成 - index.html:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorWasmOnlyTest</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <script src="js/test.js"></script>
</head>

<body>
    <app>Loading...</app>

    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">????</a>
    </div>
    <script src="_framework/blazor.webassembly.js"></script>
</body>

</html>

运行一次会在我的机器上产生以下输出:

从JS,求和时间:0.0037800000282004476 s

调用 js 和求和的 C# 时间:7.000 s

这似乎是一个相当高的开销时间......有谁知道是否有办法加快这个速度(真正的函数做了我目前在 Blazor/C# 中无法做到的事情 - 在Leaflet 中更新一个层)

编辑: 我试过here描述的同步方法,执行时间没有任何区别。

c#:

    var jsInProcess2 = (IJSInProcessRuntime)JSRuntime;
    jsInProcess2.InvokeVoid("testSumArray", array);

js: javascript 与上面的testSumArray 相同。

编辑 2:

我已尝试通过同步互操作传递 JSON 字符串:

c#:

    var jsInProcess3 = (IJSInProcessRuntime)JSRuntime;
    var array_json3 = System.Text.Json.JsonSerializer.Serialize(array);
    jsInProcess3.InvokeVoid("testSumArray3", array_json);

js:

function testSumArray3(array_json_string) {
    var t0 = performance.now();
    var array = JSON.parse(array_json_string);
    var s = sumArray(array);
    var t1 = performance.now();
    console.log('From JS, time to sum: ' + (t1 - t0) / 1000 + ' s');
    console.log('Array sum = ' + s);
}

并使用 JSON 字符串和 InvokeUnmarshalled js 互操作调用:

c#:

    var jsInProcess4 = (Microsoft.JSInterop.WebAssembly.WebAssemblyJSRuntime)JSRuntime;
    var array_json4 = System.Text.Json.JsonSerializer.Serialize(array);
    jsInProcess4.InvokeUnmarshalled<string,string>("testSumArray4", array_json4);

js:

function testSumArray4(array_mono_json_string) {
    var t0 = performance.now();
    const array_json_string = BINDING.conv_string(array_mono_json_string);
    var array = JSON.parse(array_json_string);
    var s = sumArray(array);
    var t1 = performance.now();
    console.log('From JS, time to sum: ' + (t1 - t0) / 1000 + ' s');
    console.log('Array sum = ' + s);
}

所有方法都需要大约相同的时间,6-7 秒才能完成(在 javascript 函数中大约需要 0.0015-0.006 秒)。

我试图弄清楚如何使用 BINDING.mono_array_to_js_array 在这个 file 中找到的调用 unmarshalled 传递一个数组,但这会引发一个很长的错误。 时间:

    var sum = jsInProcess4.InvokeUnmarshalled<double[],double>("testSumArray5",array)

js:

function testSumArray5(array_in) {
    var t0 = performance.now();
    var array = BINDING.mono_array_to_js_array(array_in);
    console.log(array[0]);
    var s = sumArray(array);
    var t1 = performance.now();
    console.log('From JS, time to sum: ' + (t1 - t0) / 1000 + ' s');
    console.log('Array sum = ' + s);
    return s;
}

【问题讨论】:

  • 你用的是什么浏览器? WebAssembly 是慢还是快,取决于浏览器的 WebAssembly 实现。所以比其他人更好......
  • @Vencovsky 刚刚用 chrome 进行了 quickltmy 测试..
  • 不确定“quickltmy”是什么意思
  • 很快就是我的意思:) - 自动拼写器..
  • Chrome 7 秒,Fireforx 6 秒,Edge (v. 44) 崩溃..

标签: javascript performance blazor blazor-webassembly


【解决方案1】:

刚刚找到在js中使用.net字节或浮点数组的方法。

c#:

[Inject] //Injected JSRuntime from Blazor DI
private IJSRuntime JSRuntime { get; set; }

byte[] bytes1;
float[] floats2;
...
if (JSRuntime is IJSUnmarshalledRuntime webAssemblyJSRuntime)
{
    webAssemblyJSRuntime.InvokeUnmarshalled<byte[], float[], object> 
        ("downloadArray", bytes1, floats2);
}

JavaScript:

function downloadArray(bytes1, floats2) {
    // Easy way to convert Uint8 arrays
    var byteArray = Blazor.platform.toUint8Array(bytes1);

    // Adapted method above for float32
    var m = floats2 + 12;
    var r = Module.HEAP32[m >> 2]
    var j = new Float32Array(Module.HEAPF32.buffer, m + 4, r);
}

这里的结果是 Uint8Array 和 Float32Array 对象分别来自 byte[] 和 float[] 在合理的时间内。

可能有任何方法来获取 js 数组,因为您可以从 ArrayBuffers 访问整个 .net 堆,例如 Module.HEAPU8(Uint8Array 内的堆)或 Module.HEAPF32(Float32Array 内的堆),并且可以通过指针轻松访问对象来自 InvokeUnmarshalled 参数。

【讨论】:

  • 非常感谢,这确实加快了速度!
猜你喜欢
  • 2021-06-08
  • 2022-01-27
  • 2021-04-21
  • 2021-11-06
  • 2021-04-07
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多