- O novo motor io_uring do .NET substitui o epoll no Linux, reduzindo drasticamente o uso de CPU e chamadas de sistema em servidores de alta concorrência.
- Startups e empresas brasileiras que hospedam aplicações .NET na nuvem podem reduzir custos de infraestrutura ativando o io_uring para lidar com mais tráfego usando menos hardware.
- A implementação do io_uring traz recursos de ponta como zero-copy send e multishot recv, acelerando o tempo de resposta em microsserviços e APIs.
- O suporte exclusivo ao modo SQPOLL no io_uring permite processar requisições de rede com zero system calls, essencial para sistemas financeiros e de pagamentos em tempo real no Brasil.
- A ativação do io_uring é feita de forma segura via variáveis de ambiente em distribuições Linux com kernel 5.13 ou superior, contando com fallback automático para garantir a estabilidade do servidor.
O io_uring é uma interface de entrada e saída (I/O) assíncrona moderna do kernel Linux, projetada para substituir mecanismos antigos como o epoll. Sua principal função é permitir que aplicações leiam e escrevam dados na rede ou em disco de forma extremamente rápida, utilizando anéis de memória compartilhada (ring buffers) entre o espaço do usuário e o kernel. Isso elimina a necessidade de fazer chamadas de sistema (system calls) constantes, que são operações custosas e criam gargalos em servidores de alta demanda.
A introdução do suporte nativo ao io_uring na camada de sockets do .NET muda fundamentalmente como a plataforma lida com o tráfego de rede no Linux. Ao contornar o epoll, o .NET agora consegue despachar operações de rede diretamente para o kernel via C#, reduzindo drasticamente a latência e o consumo de recursos em aplicações web, microsserviços e gateways de alta concorrência.
Principais novidades
A arquitetura do novo motor de I/O foi construída com foco em minimizar transições entre o kernel e o espaço de usuário. O código nativo (em C) foi reduzido a um shim de apenas 333 linhas, enquanto todo o gerenciamento de submissões e conclusões foi portado para código gerenciado em C#.
A atualização traz a implementação do padrão completion-mode, substituindo a abordagem baseada em prontidão do epoll. Com o multishot accept e o multishot recv, o runtime do .NET arma uma única requisição no kernel que se mantém persistente, gerando múltiplos eventos de rede automaticamente sem a necessidade de rearmar o loop a cada novo dado recebido.
Outro destaque é a inclusão do zero-copy send (SEND_ZC e SENDMSG_ZC). Para cargas úteis de rede superiores a 16KB, o kernel passa a utilizar referências de páginas de memória em vez de copiar os dados fisicamente para os buffers de rede. Isso tem impacto direto na vazão de servidores gRPC e transmissões de payloads massivos.
Para cenários de latência ultrabaixa, foi introduzido o suporte ao SQPOLL. Quando ativado, uma thread dedicada do kernel fica em loop monitorando a fila de submissão. Na prática, isso permite que o .NET envie dados pela rede realizando zero system calls, reduzindo o tempo de latência de submissão em até 100 vezes no percentil P99 sob carga sustentada.
Impacto e repercussão
A comunidade de alta performance aguardava essa integração há anos. Fóruns técnicos como o Hacker News e discussões no repositório oficial apontam que o .NET agora possui uma implementação de io_uring mais completa e robusta do que muitos de seus competidores diretos. O framework se posiciona à frente do Go (que ainda depende do epoll na biblioteca padrão), Node.js e até de ecossistemas Rust (como o tokio-uring, que carece de suporte a multishot e SQPOLL). A comparação mais próxima em maturidade é o Netty 4.2 do ecossistema Java, embora o .NET traga vantagens na integração direta com o coletor de lixo e o runtime.
Testes preliminares e benchmarks estruturados no pull request indicam melhorias dramáticas no servidor web Kestrel. Em workloads HTTP/1.1 (como o TechEmpower Plaintext), estima-se uma redução de 15% a 40% no custo de CPU por requisição, atacando diretamente o overhead de syscalls. Em servidores ociosos com alto volume de conexões simultâneas (como hubs WebSocket com mais de 10.000 clientes conectados), o uso de pools de buffers compartilhados pelo io_uring projeta uma redução de 30% a 50% no consumo de memória RAM, comparado ao modelo de buffers pinados por conexão do epoll.
Resumo técnico
- Negociação progressiva de flags (SQPOLL, DEFER_TASKRUN, SINGLE_ISSUER, COOP_TASKRUN) durante o setup do anel.
- Mapeamento mmap direto na memória gerenciada para SQ ring, CQ ring e array de SQE, evitando marshaling e P/Invoke no caminho quente de execução.
- Registro em lote de arquivos (IORING_REGISTER_FILES) e de buffers (IORING_REGISTER_BUFFERS), eliminando operações atômicas fget/fput no kernel por cada I/O.
- Dimensionamento adaptativo de buffers gerenciados pelo kernel com base na utilização em tempo real (inicialmente desativado por padrão para estabilidade).
- Separação de arrays de conclusão (hot e cold paths) para manter a eficiência do cache L1/L2 da CPU.
- Estrutura lock-free para filas de comunicação entre threads (MPSC) com preenchimento de linhas de cache para evitar false sharing.
- Telemetria robusta exposta via EventSource, incluindo 8 contadores de desempenho estáveis e 17 contadores diagnósticos para monitoramento de overlow e exaustão de slots.
Disponibilidade
O pull request foi aprovado e mesclado na branch principal do dotnet (dotnet:main) e fará parte de lançamentos futuros do runtime.
A engine do io_uring requer o kernel Linux 5.13 ou superior como base, mas recursos avançados exigem kernels mais modernos (multishot accept a partir do 5.19, multishot recv e zero-copy a partir do 6.0).
Para garantir a estabilidade do ecossistema, o recurso será introduzido como opt-in no primeiro momento. Desenvolvedores precisarão ativá-lo explicitamente definindo a variável de ambiente DOTNET_SYSTEM_NET_SOCKETS_IO_URING=1. O modo avançado de latência ultrabaixa requer ativação dupla, combinando a variável DOTNET_SYSTEM_NET_SOCKETS_IO_URING_SQPOLL=1 com o switch de AppContext System.Net.Sockets.IoUring.EnableSqPoll. Em casos de falhas, limites de memlock ou falta de suporte do kernel, o sistema possui um fallback gracioso automático, retornando a operação normal para a camada nativa do epoll.
