You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无Blazor的WASM处理大型数值数组性能过慢问题咨询

优化C# WASM与JavaScript间大型数组传递性能的关键要点

核心问题在于默认的数组传递方式会触发JS与WASM内存间的完整数据拷贝,100万元素的double数组单份就占8MB内存,两次输入加一次输出的拷贝开销直接拖慢了性能。以下是针对性的优化方案:

1. 使用Span/ReadOnlySpan替代数组传递

Span<T>和ReadOnlySpan<T>可以直接映射WASM内存块,避免不必要的拷贝操作,让C#方法直接操作JS端的TypedArray内存。

C#代码修改:

[JSInvokable]
public static Span<double> Test(ReadOnlySpan<double> a, ReadOnlySpan<double> b)
{
    double[] result = new double[a.Length];
    // 示例逻辑:数组元素相加,可替换为你的实际业务逻辑
    for (int i = 0; i < a.Length; i++)
    {
        result[i] = a[i] + b[i];
    }
    return result.AsSpan();
}

JavaScript调用修改:

不要使用普通JS数组,改用Float64Array(对应C#的double类型)直接传递,.NET runtime会自动映射为Span,无需拷贝:

// 假设a、b是预先准备好的Float64Array
const temp = dotnet.MyTest.test(a, b);
// temp为映射WASM内存的Float64Array,可直接使用

2. 直接操作WASM共享内存(极致优化)

通过手动分配WASM内存并传递内存偏移量,彻底消除拷贝环节,让C#和JS直接同一块内存区域读写。

C#代码修改(unsafe模式):

using System.Runtime.InteropServices;

[JSInvokable]
public static unsafe void Test(int aOffset, int length, int bOffset, int resultOffset)
{
    double* aPtr = (double*)IntPtr.Zero + aOffset / sizeof(double);
    double* bPtr = (double*)IntPtr.Zero + bOffset / sizeof(double);
    double* resultPtr = (double*)IntPtr.Zero + resultOffset / sizeof(double);

    // 直接在共享内存上计算,无拷贝
    for (int i = 0; i < length; i++)
    {
        resultPtr[i] = aPtr[i] + bPtr[i];
    }
}

JavaScript调用逻辑:

const elementSize = 8; // double类型占8字节
const length = 1000000;

// 分配WASM内存块
const aPtr = Module._malloc(length * elementSize);
const bPtr = Module._malloc(length * elementSize);
const resultPtr = Module._malloc(length * elementSize);

// 将JS数组数据写入WASM内存
const aTypedArray = new Float64Array(Module.HEAPF64.buffer, aPtr, length);
aTypedArray.set(yourOriginalAArray); // yourOriginalAArray是普通JS数组或Float64Array
const bTypedArray = new Float64Array(Module.HEAPF64.buffer, bPtr, length);
bTypedArray.set(yourOriginalBArray);

// 调用C#方法,传递内存偏移和数组长度
dotnet.MyTest.test(aPtr, length, bPtr, resultPtr);

// 从WASM内存读取结果
const result = new Float64Array(Module.HEAPF64.buffer, resultPtr, length);

// 释放内存避免泄漏
Module._free(aPtr);
Module._free(bPtr);
Module._free(resultPtr);

3. 启用AOT编译

默认的JIT编译在WASM环境下对大数组操作的优化有限,启用AOT编译可将C#代码提前编译为优化后的WASM指令,大幅提升运行效率。

在项目的.csproj文件中添加配置:

<PropertyGroup>
  <RunAOTCompilation>true</RunAOTCompilation>
</PropertyGroup>

4. 避免不必要的内存分配

尽量复用已分配的内存块,比如预先在JS端分配好结果内存,让C#直接写入,减少C#端的数组创建开销;同时避免在循环内频繁创建对象或数组。

内容的提问来源于stack exchange,提问作者TDao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 12:07:02