- A linguagem Go tem quase nenhum comportamento indefinido e possui uma semântica de GC (coletor de lixo) simples
- Em Go, é possível fazer gerenciamento manual de memória, e isso pode ser feito em cooperação com o GC
- Arena é uma estrutura de dados para alocar memória de forma eficiente quando ela tem o mesmo tempo de vida, e o texto explica como implementar isso em Go
- Explica como o GC gerencia a memória por meio do algoritmo Mark and Sweep
- É possível melhorar o desempenho de alocação de memória usando Arena, e isso pode ser alcançado com várias otimizações
- Busca melhorar o desempenho e minimizar a carga do GC por meio de remoção de write barrier, reutilização de memória, chunk pooling etc.
- Apresenta padrões seguros e rápidos para processamento real de memória em grande escala com recursos como implementação de realloc, reutilização de Arena e inicialização (Reset)
Visão geral da alocação manual de memória baseada em Arena em Go
- Go é uma linguagem segura graças ao comportamento claro do GC e à quase ausência de Undefined Behavior
- Usando o pacote
unsafe, é possível fazer controle direto da memória ajustado à implementação interna do GC
- Este texto explica como criar em Go um alocador de memória baseado em Arena que coopera com o GC
Definição de Arena e por que ela é necessária
- Arena é uma estrutura para alocar com eficiência objetos com o mesmo tempo de vida
- Enquanto o
append comum expande arrays de forma exponencial, a Arena adiciona novos blocos e fornece ponteiros
- A interface padrão é a seguinte:
Alloc(size, align uintptr) unsafe.Pointer
Ponteiros e como o GC funciona
- O GC funciona rastreando (mark) e recuperando (sweep) a memória
- Para um GC preciso, ele usa metadados chamados pointer bits, que informam onde os ponteiros estão localizados
- Se os ponteiros forem tratados incorretamente em uma Arena, o GC pode deixar de rastreá-los, o que pode causar erros de use-after-free
Como projetar uma Arena
- A estrutura Arena tem campos como:
- Todas as alocações são tratadas com alinhamento de 8 bytes e, se faltar espaço, um novo chunk é criado com
nextPow2
- Em vez de alocar o chunk como
[]uintptr, ele é alocado com o tipo struct { A [N]uintptr; P *Arena }, permitindo que o GC rastreie a Arena
Como garantir a segurança de ponteiros na Arena
- Quando são usados apenas ponteiros alocados dentro da Arena, o GC mantém a Arena inteira viva
- Configura-se o ponteiro para referenciar a Arena, garantindo que toda a Arena sobreviva ao GC
- O método de alocação da Arena faz o seguinte:
- em
allocChunk(), armazena o ponteiro da Arena no final do chunk
Resultados dos benchmarks de desempenho
- Em comparação com
new, a alocação com Arena mostrou em média ganho de desempenho de 2 a 4 vezes ou mais
- Mesmo em situações com alta carga de GC, a abordagem com Arena mostrou desempenho mais de 2 vezes melhor
- Otimizações como remoção de write barrier e uso de
uintptr oferecem até 20% de melhora em alocações pequenas
Estratégias de reutilização de chunk e eliminação de heap
- É possível reutilizar chunks com
sync.Pool
- Com
runtime.SetFinalizer(), os chunks podem ser devolvidos ao pool quando a Arena desaparecer
- O desempenho melhora bastante em alocações pequenas, mas em alocações grandes pode ficar mais lento que
new
Inicialização e reutilização da Arena
- O método
Reset() pode devolver a Arena ao estado inicial
- Embora seja arriscado, isso permite reutilizar a mesma estrutura sem realocar memória
- Mesmo na reutilização, os chunks também são reaproveitados, aumentando bastante o desempenho
Implementação da funcionalidade Realloc
- A Arena implementa a funcionalidade
realloc, permitindo expansão dinâmica para a alocação mais recente
- Quando isso não é possível, uma nova área de memória é alocada e o conteúdo é copiado
Conclusão e código completo fornecido
- Com um entendimento profundo do mecanismo de GC do Go e com base em sua implementação interna, o texto conclui um gerenciador de memória baseado em Arena
- É uma estrutura que oferece segurança e desempenho ao mesmo tempo e, se usada corretamente, é muito útil para lidar com estruturas de dados em larga escala
- O código completo inclui a struct Arena e elementos como
New, Alloc, Reset, allocChunk, finalize etc.
1 comentários
Comentários no Hacker News
Este artigo é uma leitura divertida
Reference[T]que oferece a mesma funcionalidadeRecentemente, ao fazer tuning de performance em Go, acabei usando um design de arena muito parecido para maximizar o desempenho
Alguns pontos de melhoria fáceis
unsafe.Stringé útil para passar strings a partir de slices de bytes sem alocaçãoNão é sobre o tema, mas gostei do minimapa na lateral
Resumo para quem não quer ler o artigo longo
unsafe.Pointer, o GC não consegue enxergar direito o que está sendo apontado dentro da arena e pode liberar isso por enganoreflect.StructOfpara criar um novo tipo que inclua campos de ponteiro adicionais neste blocoRelacionado: discussão sobre adicionar "regiões de memória" à biblioteca padrão
Conteúdo interessante
Go prioriza não quebrar o ecossistema
Uma nota meta simples