Capítulo 15: I/O de Alta Performance, Streams e System.Text.Json

Especialização em Sistemas com C# • C# 12 & .NET 8 • POO Moderna, LINQ, Concorrência e Arquitetura Corporativa


🗺️ Mapa Conceitual de I/O em Streams e Serialização Source Generated

flowchart TD
    Origem["Dados de Entrada / Saída"] --> Streams["Camada de Streams (System.IO.Stream)"]
    Origem --> Serializacao["Camada de Serialização JSON"]

    subgraph STREAM_PIPELINE["Streams Contíguos e Assíncronos"]
        FileStream["FileStream (Leitura/Escrita em Disco com useAsync: true)"]
        MemoryStream["MemoryStream (Buffers de Bytes em RAM)"]
        Pipe["System.IO.Pipelines (PipeReader / PipeWriter de Alta Vazão)"]
    end

    subgraph JSON_ENGINE["Evolução da Serialização JSON no .NET"]
        Newtonsoft["Newtonsoft.Json (Histórico: Reflexão Pesada, Lento)"]
        STJ_Reflection["System.Text.Json Clássico (Reflexão com Caches)"]
        STJ_SourceGen["System.Text.Json + Source Generators (C# 10+: Zero Reflexão)"]
    end

    Streams --> STREAM_PIPELINE
    Serializacao --> JSON_ENGINE

    STJ_SourceGen --> NativeAOTReady["Compatibilidade 100% com Native AOT e Zero Garbage"]

    style Origem fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style STREAM_PIPELINE fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style STJ_SourceGen fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style NativeAOTReady fill:#fff3e0,stroke:#ff9800,stroke-width:2px

🏛️ 1. A Arquitetura de Streams em .NET

A classe abstrata System.IO.Stream é o alicerce fundamental para transferência de sequências de bytes no ecossistema .NET. Diferente de ler um arquivo inteiro para uma string na memória (File.ReadAllText), o uso de streams permite processar gigabytes de dados em fatias contínuas (chunking), mantendo o consumo de memória RAM estável em poucos kilobytes.

Boas Práticas Mandatórias em FileStream:

// Configuração de Alto Rendimento para I/O em Disco:
await using FileStream stream = new(
    caminhoArquivo,
    FileMode.OpenOrCreate,
    FileAccess.ReadWrite,
    FileShare.Read,
    bufferSize: 4096,
    useAsync: true // Habilita I/O completion ports (IOCP) do SO sem travar threads!
);

⚙️ 2. System.Text.Json vs Newtonsoft.Json: O Fim da Reflexão em Runtime

Por mais de uma década, a biblioteca externa Newtonsoft.Json (Json.NET) foi o padrão da indústria. No entanto, sua arquitetura é baseada em reflexão profunda em tempo de execução (System.Reflection), gerando:

  1. Alocações frequentes de strings UTF-16 intermediárias.
  2. Incompatibilidade fundamental com Native AOT (onde os metadados de reflexão são podados).
  3. Gargalo de CPU em microsserviços sob alta concorrência.

O System.Text.Json introduzido pela Microsoft opera diretamente sobre bytes UTF-8, evitando a conversão intermediária para UTF-16 e reduzindo o consumo de memória em mais de 50%.


📦 3. JSON Source Generators no .NET 8

No .NET 8, a abordagem recomendada para microsserviços corporativos é a geração de código em tempo de compilação via JsonSerializerContext:

using System.Text.Json.Serialization;

// 1. DTO de Domínio
public record DispositivoTelemetria(string Id, double Voltagem, bool Online);

// 2. Declaração do Contexto Source Generated
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(DispositivoTelemetria))]
[JsonSerializable(typeof(List<DispositivoTelemetria>))]
public partial class TelemetriaContext : JsonSerializerContext
{
}

O Que Acontece nos Bastidores:

Durante a compilação (dotnet build), o Roslyn lê a estrutura de DispositivoTelemetria e gera código C# puro contendo chamadas manuais ultra-rápidas a Utf8JsonWriter e Utf8JsonReader. Em tempo de execução, a serialização executa sem nenhuma reflexão e sem inspeção dinâmica de tipos.


📊 4. Matriz Comparativa: Desempenho de Serialização JSON

Critério Newtonsoft.Json System.Text.Json (Reflexão) System.Text.Json (Source Gen)
Formato Base de I/O UTF-16 (string) UTF-8 nativo (byte[]) UTF-8 nativo (ReadOnlySpan<byte>)
Mecanismo de Análise Reflexão em Runtime Reflexão com Cache Código C# Estático em Build
Alocação de Memória Alta Baixa Mínima / Zero no Hot Path
Compatível com AOT? Não Não (requer warnings) 100% Compatível e Otimizado
Velocidade de Execução Linha de Base (1x) 2x a 3x mais rápido 4x a 5x mais rápido

🔬 5. Laboratório de Arquitetura: Leitura Zero-Allocation com Utf8JsonReader

Para cenários onde um gateway precisa inspecionar apenas um cabeçalho JSON em um payload gigante sem desserializar o documento inteiro, a struct de baixo nível Utf8JsonReader permite navegar nos tokens diretamente a partir de um ReadOnlySpan<byte>:

using System.Text.Json;

public static string ExtrairStatusSensor(ReadOnlySpan<byte> jsonBytesUtf8)
{
    Utf8JsonReader reader = new(jsonBytesUtf8);
    
    while (reader.Read())
    {
        if (reader.TokenType == JsonTokenType.PropertyName && reader.ValueTextEquals("status"u8))
        {
            reader.Read(); // Avança para o valor da propriedade
            return reader.GetString() ?? "";
        }
    }
    return "UNKNOWN";
}

O literal de string UTF-8 ""u8 (C# 11) emite os bytes UTF-8 em tempo de compilação na seção .rodata, permitindo que reader.ValueTextEquals compare sequências de bytes com vetorização SIMD sem criar nenhuma string na memória RAM!