Trabalho Avançado com WebAssembly e C#: da Teoria à Otimização
WebAssembly (Wasm) tornou-se uma tecnologia chave para executar código de alto desempenho no navegador. No ecossistema .NET, a principal ferramenta para trabalhar com Wasm é o Blazor, no entanto, para resolver problemas complexos, é necessário um profundo entendimento dos mecanismos internos. Neste artigo, abordaremos aspectos avançados: otimização do tempo de execução, transferência de estruturas de dados complexas, ajuste fino da compilação AOT e interação eficiente com JavaScript.
O artigo é destinado a desenvolvedores já familiarizados com os fundamentos do Blazor WebAssembly e que desejam obter o máximo desempenho e flexibilidade de suas aplicações.
1. Otimização de Desempenho: AOT vs JIT e Gerenciamento de Memória
Por padrão, o Blazor WebAssembly usa o interpretador .NET IL (MSIL) no navegador. Para trechos de código críticos para o desempenho, é necessário mudar para a compilação AOT (Ahead-of-Time).
Configuração da Compilação AOT
Adicione a flag <RunAOTCompilation>true</RunAOTCompilation> no arquivo de projeto (.csproj). Isso compilará seu código em bytecode Wasm nativo, acelerando a execução em 5 a 10 vezes, mas aumentará o tamanho da compilação.
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <RunAOTCompilation>true</RunAOTCompilation> </PropertyGroup></Project>Gerenciamento de Memória: Evite Marshalling
Cada chamada de C# para JavaScript (e vice-versa) é uma operação cara. Para transferir grandes arrays de dados, use System.Buffers.ArrayPool<T> e memória compartilhada através de MonoWasmRuntime.
using System.Buffers;using System.Runtime.InteropServices;
public class MemoryOptimizer{ public static void ProcessLargeArray(int size) { // Aluga um array do pool para evitar pressão no GC int[] buffer = ArrayPool<int>.Shared.Rent(size); try { // Seu trabalho intensivo com o array for (int i = 0; i < size; i++) { buffer[i] = i * 2; } } finally { ArrayPool<int>.Shared.Return(buffer); } }}Perfil de Desempenho
Use as ferramentas integradas do navegador (Chrome DevTools) para criar perfil do código Wasm. Ative <WasmProfiling>true</WasmProfiling> na configuração do projeto para obter stack traces legíveis.
2. Interoperabilidade JavaScript Avançada: Transferência de Objetos Complexos e Chamadas Sem Cópia
O IJSRuntime padrão serializa objetos em JSON, o que é lento para grandes volumes de dados. Use o trabalho direto com memória através de MonoWasmRuntime e System.Runtime.InteropServices.JavaScript.
Acesso Direto à Memória JS a partir de C#
using System.Runtime.InteropServices.JavaScript;
public partial class WasmInterop{ // Importa uma função JavaScript que retorna um ponteiro para um array [JSImport("getDataPointer", "main.js")] public static partial nint GetDataPointer(int length);
public static void ProcessExternalData(int length) { nint ptr = GetDataPointer(length); // Converte o ponteiro em um array gerenciado sem cópia Span<byte> data = new Span<byte>((void*)ptr, length); // Trabalha com os dados diretamente for (int i = 0; i < data.Length; i++) { data[i] = (byte)(data[i] ^ 0xFF); // Exemplo: inversão de bits } }}Exportação de Funções C# para Chamada a partir de JavaScript
public partial class ExportedFunctions{ [JSExport] public static int Add(int a, int b) { return a + b;