Vantagens e desvantagens do HugePages

Vantagens e desvantagens do HugePages

A tradução do artigo foi preparada para os alunos do curso Administrador Linux.

Recentemente, falei sobre como verificar e ativar o uso de Hugepages no Linux.
Este artigo será útil apenas se você realmente tiver um lugar para usar Hugepages. Encontrei muitas pessoas que se enganam com a perspectiva de que Hugepages magicamente aumentarão o desempenho. No entanto, o hugepaging é um tema complexo e, se mal utilizado, pode diminuir o desempenho.

Parte 1: Verificando se Hugepages estão ativados no Linux (original aqui)

Problema:
É necessário verificar se HugePages estão ativados no seu sistema.

Solução:
É bem simples:

cat /sys/kernel/mm/transparent_hugepage/enabled

Você receberá algo assim:

always [madvise] never

Você verá uma lista de opções disponíveis (always, madvise, never), com a opção atualmente ativa entre parênteses (por padrão, madvise).

madvise significa que transparent hugepages são ativadas apenas para áreas de memória que explicitamente solicitam hugepages com madvise(2).

always significa que transparent hugepages são ativadas sempre e para todos os processos. Normalmente, isso melhora o desempenho, mas se você tiver um cenário em que muitos processos consomem pequenas quantidades de memória, a carga total na memória pode aumentar drasticamente.

never significa que transparent hugepages não serão ativados mesmo quando solicitados com madvise. Para saber mais, consulte o documentação núcleo do Linux.

Como alterar o valor padrão

Opção 1: Mudar diretamente sysfs (após reiniciar, o parâmetro voltará ao valor padrão):

echo always > /sys/kernel/mm/transparent_hugepage/enabled
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/enabled

Opção 2: Altere o valor padrão do sistema recompilando o núcleo com uma configuração alterada (essa opção é recomendada apenas se você estiver usando um núcleo personalizado):

  • Para definir always como padrão, use:
    CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
    # Comente CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
  • Para definir madvise como padrão, use:
    CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
    # Comente CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y

Parte 2: Vantagens e desvantagens de HugePages

Tentaremos explicar seletivamente as vantagens, desvantagens e possíveis erros ao usar Hugepages. Como se trata de um artigo tecnologicamente complexo e meticuloso, provavelmente será difícil de entender para pessoas que se iludem pensando que Hugepages são uma panaceia; sacrificarei a precisão em favor da simplicidade. Vale a pena lembrar que muitos tópicos são realmente complexos e, portanto, são fortemente simplificados.

Observe que estamos falando de sistemas x86 de 64 bits que operam em Linux, e que apenas assumo que o sistema suporta transparent hugepages (já que não é uma falha que hugepages não sejam substituídos), como acontece na maioria dos ambientes Linux modernos.

Nos links abaixo, irei anexar mais descrições técnicas.

Memória virtual

Se você é um programador C++, sabe que os objetos na memória têm endereços específicos (valores de ponteiro).

No entanto, esses endereços nem sempre refletem endereços físicos na memória (endereços na RAM). Eles representam endereços na memória virtual. O processador possui um módulo especial MMU (unidade de gerenciamento de memória), que ajuda o núcleo a mapear a memória virtual para a localização física.

Esse abordagem possui várias vantagens, sendo as mais importantes:

  • Desempenho (por diversas razões);
  • Isolamento de programas, isto é, nenhum programa pode ler a memória de outro programa.

O que são páginas?

A memória virtual é dividida em páginas. Cada página individual aponta para uma determinada memória física, podendo apontar para uma área na memória RAM ou para um endereço designado a um dispositivo físico, como uma placa de vídeo.

A maioria das páginas com as quais você lida aponta tanto para RAM quanto para swap, ou seja, estão armazenadas em um disco rígido ou SSD. O núcleo gerencia a localização física de cada página. Se o acesso for feito a uma página de swap, o núcleo interrompe o fluxo que está tentando acessar a memória, lê a página do disco rígido/SSD para a memória RAM e, em seguida, continua a execução do fluxo.

Esse processo é transparente para o fluxo, ou seja, ele não lê diretamente do disco rígido/SSD. O tamanho das páginas normais é de 4096 bytes. O tamanho das Hugepages é de 2 megabytes.

Buffer de Tradução Associativa (TLB)

Quando um programa acessa uma determinada página de memória, a CPU precisa saber de qual página física ler os dados (ou seja, ter um mapa de endereços virtuais).

No núcleo, existe uma estrutura de dados (tabela de páginas) que contém todas as informações sobre as páginas utilizadas. Através dessa estrutura de dados, é possível mapear o endereço virtual para o endereço físico.

No entanto, a tabela de páginas é bastante complexa e funciona lentamente, então simplesmente não podemos analisar toda a estrutura de dados cada vez que um processo acessa a memória.

Felizmente, nosso processador tem um TLB que cacheia a correspondência entre endereços virtuais e físicos. Isso significa que, embora precisemos analisar a tabela de páginas na primeira tentativa de acesso, todos os acessos subsequentes à página podem ser processados no TLB, garantindo um desempenho rápido.

Como ele é implementado como um dispositivo físico (o que o torna rapidamente), sua capacidade é limitada. Portanto, se você quiser acessar mais páginas, o TLB não conseguirá armazenar a correspondência para todas elas, resultando em um desempenho significativamente mais lento do seu programa.

As Hugepages vêm ao auxílio.

Então, o que podemos fazer para evitar o estouro do TLB? (Partimos do pressuposto de que o programa ainda precisa do mesmo volume de memória).

É aqui que as Hugepages entram. Em vez de 4096 bytes, que exigem uma única entrada no TLB, uma entrada no TLB agora pode apontar para enormes 2 megabytes. Vamos supor que o TLB tenha 512 entradas, aqui sem Hugepages podemos mapear:

4096 b⋅512=2 MB

Enquanto com elas podemos mapear:

2 MB⋅512=1 GB

É por isso que Hugepages são incríveis. Eles podem melhorar o desempenho sem uma aplicação significativa de esforço. Mas há avisos importantes.

Substituição de Hugepages

O kernel rastreia automaticamente a frequência de uso de cada página de memória. Se a memória física (RAM) estiver baixa, o kernel moverá páginas menos importantes (menos utilizadas) para o disco rígido para liberar parte da RAM para páginas mais importantes.
Basicamente, o mesmo se aplica às Hugepages. No entanto, o kernel pode trocar apenas páginas inteiras, e não bytes individuais.

Vamos supor que temos o seguinte programa:

char* mymemory = malloc(2*1024*1024); // Vamos considerar isso como uma Hugepage!
// Preencher mymemory com alguns dados
// Fazer muitas outras coisas,
// que levarão à substituição da página mymemory
// ...
// Solicitar acesso apenas ao primeiro byte
putchar(mymemory[0]); 

Nesse caso, o núcleo precisará recuperar 2 megabytes de informações do disco rígido/SSD apenas para que você leia um byte. No que diz respeito às páginas comuns, é necessário ler apenas 4096 bytes do disco rígido/SSD.

Portanto, se o hugepage for substituído, sua leitura ocorre mais rapidamente apenas se você precisar acessar toda a página. Isso significa que, se você estiver tentando acessar aleatoriamente diferentes partes da memória e simplesmente ler alguns kilobytes, deve usar páginas comuns e não se preocupar com mais nada.

Por outro lado, se você precisar acessar uma grande parte da memória de forma sequencial, os hugepages aumentarão seu desempenho. No entanto, você precisa verificar isso por conta própria (e não com software abstrato) e ver o que funcionará mais rápido.

Alocação de memória

Se você está programando em C, sabe que pode solicitar tamanhos tão pequenos (ou quase tão grandes) quanto necessário da pilha usando para alocação de memória pode falhar se precisar da memória RAM atualmente ocupada pelo ARC. Mas há boas razões para usar o cache próprio, pelo menos agora.. Suponha que você precise de 30 bytes de memória:

char* mymemory = malloc(30);

Para o programador, parece que você está "solicitando" 30 bytes de memória ao sistema operacional e retorna um ponteiro para alguma memória virtual. Mas, na verdade, malloc () é apenas uma função C que chama internamente as funções brk e sbrk para solicitar ou liberar memória do sistema operacional.

No entanto, solicitar cada vez mais memória para cada alocação não é eficiente; é bastante provável que algum segmento de memória já tenha sido liberado (free()), e podemos reutilizá-lo. para alocação de memória pode falhar se precisar da memória RAM atualmente ocupada pelo ARC. Mas há boas razões para usar o cache próprio, pelo menos agora. implementa algoritmos bastante complexos para reutilizar memória liberada.

Nesse caso, tudo acontece sem que você perceba, então por que isso deveria te preocupar? Porque a chamada free() não significa que a memória será imediatamente devolvida ao sistema operacional.

Existe o conceito de fragmentação de memória. Em casos extremos, há segmentos da pilha onde apenas alguns bytes são usados, enquanto tudo que está entre eles foi liberado. (free()).

Observe que a fragmentação de memória é um tópico extremamente complexo, e até mesmo pequenas mudanças no programa podem afetá-la significativamente. Na maioria das vezes, os programas não causam uma fragmentação de memória considerável, mas você deve ter em mente que, se houver um problema com a fragmentação em alguma área da pilha, os hugepages podem apenas agravar a situação.

Aplicação seletiva de hugepages

Após ler o artigo, você identificou quais partes do seu programa podem se beneficiar do uso de hugepages e quais não. Portanto, deve-se realmente habilitar os hugepages?

Felizmente, você pode usar madvise(), para habilitar o hugepaging apenas para as áreas de memória onde será útil.

Para começar, verifique se os hugepages estão funcionando no modo madvise(), usando documentação no início do artigo.

Em seguida, use madvise(), para indicar ao núcleo onde exatamente usar os hugepages.

#include <sys/mman.h>
// Аллоцируйте большое количество памяти, которую будете использовать
size_t size = 256*1024*1024;
char* mymemory = malloc(size);
// Просто включите hugepages…
madvise(mymemory, size, MADV_HUGEPAGE);
// … и задайте следующее
madvise(mymemory, size, MADV_HUGEPAGE | MADV_SEQUENTIAL)

Observe que esse método é apenas uma recomendação ao núcleo para o gerenciamento de memória. Isso não significa que o núcleo usará automaticamente os hugepages para a memória fornecida.

Consulte a documentação (manpage) madvise, para saber mais sobre gerenciamento de memória e madvise(), esse tópico tem uma curva de aprendizado extremamente íngreme. Portanto, se você realmente pretende entendê-lo bem, prepare-se para ler e testar por algumas semanas antes de esperar qualquer resultado positivo.

O que ler?

Tem alguma dúvida? Deixe um comentário!

Fonte: habr.com

Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS 🔥 Compre hospedagem confiável para sites com proteção contra DDoS, servidores VPS VDS | ProHoster