Capítulo 15: I/O de Alta Performance, Streams e System.Text.Json
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:
- Alocações frequentes de strings UTF-16 intermediárias.
- Incompatibilidade fundamental com Native AOT (onde os metadados de reflexão são podados).
- 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!