(Tradução): Em defesa do swap, equivocos comuns

Table of Contents

Introdução

Salve galera, desculpa a demora para trazer novos posts, ultimamente estive ocupado fazendo any coisas no meu privado que não tive o prazer de compartilhar com vocês.

Enfim, esse não tão pedido de desculpas foi para trazer a tradução de um post que eu acho super relevante na área de Osdev In defence of swap: common misconceptions.

Tradução

Tl;Dr

  1. Ter swap é uma parte razoavelmente importante de um sistema que funciona bem. Sem ele, o gerenciamento sensato de memória se torna bem mais díficil de alcançar.

  2. A swap geralmente não visa obter memória de emergência, mas sim tornar a recuperação de memória igualitária e eficiente. Na verdade, usá-lo como memória de emergência geralmente é ativamente prejudicial.

  3. Evitar o swap não evita que o I/O do disco se torne uma problema na contenção de memória. Em vez disso, ele simplesmente muda a movimentação do I/O do disco de páginas anônimas para páginas de arquivo. Isso não só pode ser menos eficiente, já que temos um conjunto menor de páginas para selecionar para recuperação, mas também pode contribuir para entrar nesse estado de alta contenção em primeiro lugar.

  4. O swapper em kerneis anteriores ao 4.0 tem muitas armadilhas e contribuiu para percepção negativa de muitas pessoas sobre o swap devido à sua ânsia excessiva em trocar páginas. Em kerneis superiores ao 4.0, a situação é infinitamente melhor.

  5. Em SSD’s, a troca de páginas anônimas e a recuperação de páginas de arquivos são essencialmente equivalentes em termos de desempenho e latência. Em discos HD, as leituras de trocas são mais lentas devido a leituras aleatórias, portanto, uma vm.swappiness em configuração mais baixa faz sentido. *Procure mais sobre swappines

  6. Desabilitar o swap não evita comportamento patológico próximo ao OOM, embora seja verdade que ter swap pode prolongá-lo. Quer o OOM Killer global seja invocado com ou sem swap ou tenha sido invocado mais cedo ou mais tarde, o resultado é o mesmo: Você fica com um sistema em estado imprevisível. Não ter swap não evita isso

  7. Você pode obter um melhor comportamento de swap sob pressão de memória e evitar thrashing usando memory.low e friends no cgroups v2.


Como parte do meu trabalho para melhorar o gerenciamento de memória do kernel e o cgroup v2, tenho conversado com muitos engenheiros sobre atitudes em relação à memória, especialmente em relação ao comportamento do aplicativo sobre pressão e às heurísticas do sistema operacional usadas nos bastidores para gerenciamento de memória.

Um tópico repetido nessas discussões foi o swap. Swap é um tema muito contestado e pouco compreendido, mesmo por quem trabalha com Linux há muitos anos.Muitos o consideram inútil ou ativamente prejudicial: uma relíquia de uma época que a memória era escassa e os discos eram um mal necessário para fornecer o tão necessário espaço para paginação. Esta é uma afirmação que ainda vejo sendo discutida com relativa frequência nos últimos anos, e tive muitas discussões com colegas, amigos e colegas da indústria para ajudá-llos a entender porque swap ainda é um conceito útil em computadores modernos com significativamente mais memória física disponível do que no passado.

Há também muitos mal-entendidos sobre o propósito da swap - muitas pessoas apenas o veem como uma espécie de “memória extra lenta” para uso em emergências, mas não entendem como ela pode contribuir durante a carga normal para uma operação saudável de um sistema operacional como um todo.

Muitos de nós já ouvimos a maioria dos tropos usuais sobre a memória: “O Linux usa muita memória”, “a swap deve ser o dobro do tamanho da memória física” e assim por diante. Embora estes sejam triviais de dissipar, ou a discussão em torno deles tenha se tornado mais sutil nos últimos anos, o mito da swap “inútil” é muito mais baseado em heurísticas e arcanos do que em algo que pode ser explicado por simples analogia, e requer um pouco mais de compreensão do gerenciamento de memória para raciocinar.

Este post é voltado principalmente para aqueles que administram sistemas Linux e estão interessados em ouvir os contrapontos de rodar com subdimensionado/sem swap ou rodar com vm.swappiness = 0.


Fundo

É difícil falar sobre porque swap e troca de páginas é uma coisa boa na operação normal sem um entendimento compartilhado de alguns dos mecanismos básicos subjacentes em jogo no gerenciamento de memória do Linux, então vamos ter certeza de que estamos na mesma página.


Tipos de memória

Existem muitos tipos diferentes de memória no Linux e cada tipo possui suas propriedades. Compreender as nuances disso é fundamental para entender porque a swap é importante.

Por exemplo, existem páginas que são blocos de memória normalmente 4k responsáveis por armazenar o código de cada processo executado em seu computador. Existem também páginas responsáveis por armazenar em cache dados e metadados relacionados aos arquivos acessados por programas, a fim de agilizar acessos futuros. Eles fazem parte do cache de páginas a qual me referirei como memória de arquivo.

Existem também páginas que são responsáveis pelas alocações de memória feitas dentro desse código por exemplo, quando uma nova memória que foi alocada por malloc é gravada ou ao usar o mmap com o sinalidade MAP_ANONYMOUS. Essas são as páginas anônimmas - assim chamadas por que não são apoiadas por nada - e me referirei a elas como memória anônima.

Existem outros tipos de memória também - memória compartilhada, memória slab, stack, buffers e similares - mas a memória anônima e a memória de arquivo são as mais conhecidas e fáceis de entender, então vou usá-las em meus exemplos, embora se apliquem igualmente a esses tipos.


Memória recuperável/não recuperável

Uma das questões mais fundamentais quando se pensa sobre um determinado tipo de memória é se ela pode ser recuperada ou não. “Recuperar” aqui significa que o sistema pode, sem perder dados, limpar páginas desse tipo de memória física.

Para alguns tipos de páginas, isso normalmente é bastante trivial. Por exemplo, no caso de memória cache de página limpa (não modificada), estamos simplesmente armazenando em cache algo que temos no disco para desempenho, para que possamos descartar a página sem precisar realizar nenhuma operação especial.

Para alguns tipos de páginas, isso é possivel, mas não trivial. Por exemplo, no caso de memória cache de página suja (que foi modificada), não podemos simplesmente descartar a página, porque o disco ainda não contém nossas modificações. Como tal, precisamos negar a recuperação ou primeiro recuperar nossas alterações no disco antes de podermos descartar essa memória.

Para alguns tipos de páginas, isso não é possivel. Por exemplo, no caso das páginas anônimas mencionadas anteriormente, elas só existem na memória e em nenhum outro armazenamento de apoio, portanto devem ser mantidas lá.


Sobre a natureza da swap

Se você procurar descrições do propósito da swap no Linux, inevitalmente encontrará muitas pessoas falando sobre isso como se fosse apenas uma extensão da RAM física para uso em emergências. Por exemplo, aqui está uma postagem aleatória que obtive como um dos principais resultados ao digitar “o que é swap” no Google:

A swap é essencialmente uma memória de emergência; um espaço reservado para momentos em que seu sistema precisa temporariamente de mais memória física do que na RAM. É considerado “ruim” no sentido de que é lento e ineficient, e se o seu sistema precisa usar swap constantemente, então obviamente ele não tem memória suficiente. […] Se você tem RAM suficiente para atender a todas as suas necessidades e não espera maximizá-la, então você estará perfeitamente seguro executando sem um espaço de swap.

Para ser claro, não culpo o autor deste comentário pelo conteúdo de sua postagem - isso é aceito como “conhecimento comum” por muitos administradores de sistemas Linux e é provavelmente uma das coisas mais prováveis que você ouvirá de um se você pedir que falem sobre swap. Infelizmente, porém, também é um mal-entendido sobre o propósito e o uso da swap, especialmente em sistemas modernos.

Acima, falei sobre a recuperação de páginas anônimas ser “impossível”, já que as páginas anônimas, por sua natureza, não tem nenhum armazenamento de apoio ao qual recorrer quando são eliminadas da memória - como tal, sua recuperação resultaria na perda completa de dados dessa página. E se pudessemos criar um armazenamento para essas páginas?

Bem, é exatamente para isso que serve a swap. Swap é uma área de armazenamento para essas páginas aparentemente “irrecuperáveis” que nos permite paginá-las para um dispositivo de armazenamento sob demanda. Isso significa que eles agora podem ser considerados igualmente elegíveis para recuperação como os outros tipos de memória mais triviais, como páginas de arquivos limpas, permitindo um uso mais eficiente da memória física disponível.

A troca é principalmente um mecanismo para igualdade de recuperação, não para “memória extra” de emergência. A troca não é o que torna seu aplicativo lento - entrar na contenção geral de memória é o que torna seu aplicativo lento.

Então, em que situações nesse cenário de “igualdade de recuperação”, escolheriamos legitamente recuperar páginas anônimas? Aqui estão abstratamente, alguns cenários não incomuns:

  1. Durante a inicialização, um programa de longa duração pode alocar e usar muitas páginas. Essas páginas também podem ser usadas como parte do desligamento/limpeza, mas não são necessárias quando o programa é “iniciado” (no sentido específico do aplicativo). Isso é bastante comum para daemons que possuem dependências significativas para inicializar.

  2. Durante a operação normal do programa, podemos alocar memória que raramente é usada. Pode fazer mais sentido para o desempenho geral do sistema exigir uma “falha grave” para pagina-los do disco sob demanda, em vez de usar a memória para outra coisa que seja mais importante.


Examinando o que acontece com/sem swap

Vejamos situações típicas e como elas funcionam com e sem swap presente. Falo sobre métricas em torno de “contenção de memória” em minha paletra no cgroup v2.

Sob nenhuma/pouca contenção de memória
Sob contenção de memória moderada/alta
Sob picos temporários no uso de memória

OK, quero trocar o sistema como posso ajustá-lo para aplicativos individuais?

Você não achou que conseguiria ler todo esse post sem que eu conectasse o cgroup v2, não é? ;-)

Obviamente, é difícil para um algoritmo heurístico genérico estar certo o tempo todo, por isso é importante que você seja capaz de orientar o kernel. Historicamente, o único ajuste que você poderia fazer era no nível do sistema, usando vm.swappiness. Isso tem dois problemas: vm.swappiness é incrivelmente difícil de raciocinar porque alimenta apenas uma parte de um sistema heurístico maior e também abrange todo o sistema, em vez de ser granular para um conjunto menor de processos.

Você também pode usar MLOCK para bloquear páginas na memória, mas isso requer modificar o código do programa, divertir-se com LD_PRELOAD ou fazer coisas horríveis com um depurador em tempo de execução. Em linguagens baseadas em VM isso também não funciona muito bem, já que geralmente você não tem controle sobre a alocação e acaba tendo que fazer MLOCKALL, o que não tem precisão em relação às páginas que realmente lhe interessam

O cgroup v2 possui um per-cgroup ajustável na forma de memória.low, o que nos permite dizer ao kernel para preferir outros aplicativos para recuperação abaixo de um certo limite de memória usada. Isso nos permite não impedir que o kernel troque partes de nosso aplicativo, mas prefira recuperá-lo de outros aplicativos sob contenção de memória. Sob condições normais, a lógica de swap do kernel é geralmente muito boa, e permitir a troca de páginas de forma oportunista geralmente aumenta o desempenho do sistema. Trocar thrash sob forte contenção de memória não é o ideal, mas é mais uma propriedade de simplesmente ficar totalmente sem memória do que um problema com o swapper. Nessas situações, normalmente processos não críticos quando a pressão da memória começa a aumentar.

Você não pode simplesmente confiar no OOM Killer para isso. O OOM Killer só é invocado em situações de falha grave, quando já entramos em um estado que o sistema está gravemente insalubre e pode muito bem estar assim há algum tempo. Você precisa lidar com a situação de forma oportunistas antes de pensar no OOM Killer.

Porém, a determinação da pressão da memória é um tanto díficil usando contadores de memória tradicionais do Linux. Temos algumas coisas que parecem um tanto relacionadas, mas são meramente tangenciais-uso de memória, varreduras de páginas, etc.-e apenas a partir dessas métrica é muito difícil distinguir uma configuração de memória eficiente de uma que tende à contenção de memória. Há um grupo nosso no Facebook, liderado por Johannes, trabalhando no desenvolvimento de novas métricas que expõem mais facilmente a pressão da memória e que devem ajudar nisso no futuro. Se você estiver interessado em saber mais sobre isso, entrarei em detalhes sobre uma nova metrica que está sendo considerada em minha palestra no cgroup v2.


Afinal

De quanta swap eu preciso, então?

Em geral, a quantidade mínima de espaço de troca necessária para um gerenciamento de memória ideal depende do número de páginas anônimas fixadas na memória que raramente são acessadas novamente por um aplicativo e no valor da reuperação dessas páginas anônimas. O último é principalmente uma questão de quais páginas não são mais eliminadas para dar lugar a essas páginas anônimas acessadas com pouca frequência.

Se você tiver muito espaço em disco e um kernel mais recente (4.0+), mais swap é quase sempre melhor do que menos. Em kerneis mais antigos kswapd, um dos processos do kernel responsáveis pelo gerenciamento de swap, era historicamente muito ansioso para trocar a memória de forma agressiva quanto mais swap você tivesse. Recentemente, o comportamento de troca quando uma grande quantidade de espaço de troca está disponível foi significativamente melhorado. Se você estiver executando o kernel 4.0+, ter uma troca excessivamente zelosa. Dessa forma, se você tiver espaço, ter um tamanho de swap de alguns GB mantém suas opções abertas em kernels modernos.

Se você estiver mais limitado com espaço em disco, a resposta realmente dependerá das compensações que você terá que fazer e da natureza do ambiente. Idealmente, você deve ter swap suficiente para fazer seu sistema operar de maneira ideal, com carga normal e de pico. O que eu recomendo fazer é configurar alguns sistemas de teste com 2 a 3 GB de swap ou mais e monitorar o que acontece ao longo de uma semana ou mais sob condições variadas de carga. Contanto que você não tenha encontrado falta severa de memória durante aquela semana - nesse caso o teste não terá sido muito útil - você provavelmente acabará com algum número de MB de swap ocupados. Como tal, provavelmente vale a peans ter pelo menos essa quantidade de troca disponível, além de um pequeno buffer, para alterar as cargas de trabalho. No modo de logging também pode mostrar quais aplicativos estão tendo suas páginas trocadas na coluna SWAPSZ, portanto, se você ainda não o usa em seus servidores para registrar o estado histórico do servidor, provavalmente desejará configurá-lo nessas máquinas de teste como modo de logging como parte deste experimento. Isso também informa quando seu aplicativo começou a trocar páginas, que você pode vincular a eventos de log ou outros dados importantes.

Outra coisa que vale a pena considerar é a natureza no meio de swap. As leituras de swap tendem a ser altamente aleatórias, pois não podemos prever comsegurança quais páginas serão refeitas e quando. Em um SSD iss não importa muito, mas em HD, a IO aleátoria é extremamente cara, pois requer movimento físico para ser alcançada. Por outro lado, o refaulting das páginas dos arquivos relacionados à operação de um único aplicativo em tempo de execução tendem a ser menos fragmentados. Isso pode significar que em um HD você pode querer direcionar mais para recuperação de páginas de arquivos em vez de trocar páginas anônimas, mas, novamente, você precisa testar e avaliar como isso se equilibra para sua carga de trabalho.

Para usuários de laptop/desktop que desejam hibernar para swap, isso também precisa ser levado em consideração - neste caso, seu arquivo de troca deve ter pelo menos o tamanho da RAM física.

Qual deve ser minha configuração de troca?

Primeiro, é importante entender o que é vm.swappiness, acontece que vm.swappiness é um sysctl que direciona a recuperação de memória pra recuperação de páginas anônimas ou para páginas de arquivo. Isso é feito usando dois atributos diferentes: file_prio (nossa disposição de recuperar páginas de arquivos) e anon_prio (nossa disposição de recuperar páginas anônimas). vm.swappiness contribui para isso, pois se torna o valor padrão para anon_prio, e também é subtraído do valor padrão de 200 para file_prio, o que significa que para um valor de vm.swappiness = 50, o resultado é anon_prio 50 e file_prio 150 (os números exatos não importam tanto quanto seu peso relativo em relação ao outro).

Isso significa que, em geral, vm.swappiness é simplesmente uma proporção de quão são os custos de recuperação e refaulting de memória anônima em comparação com a memória de arquivo para seu hardware e carga de trabalho. Quanto menor o valor, mais você informa ao kenrel que páginas anônimas acessadas com pouca frequência são caras para serem trocadas e inseridas em seu hardware. Quanto maior o valor, mais você informa que o custo de troca de páginas ânonicmas e páginas de arquivo são semelhantes em seu hardware. O subsistema de gerenciamento de memória ainda tentará decidir principalmente se troca arquivos ou páginas anônimas com base em quão quente está a memória, mas a troca inclina o cálculo de custo mais para a troca ou mais para a eliminação dos caches do sistema de arquivos quando poderia acontecer de qualquer maneira. Em SSD’s, eles são basicamente tão caros quanto os outros, portanto, a configuração vm.swapiness = 100 (igualdade total) pode funcionar bem. Em HD’s, a troca pode ser significativamente mais cara, já que a troca em geral requer leituras aleatórias, portanto, você pode querer direcionar mais para um valor mais baixo.

A realidade é que a maioria das pessoas não tem a noção do que seu hardware exige, então não é trivial ajustar esse valor apenas com base no seu instinto - isso é algo que você precisa testar usando valores diferentes. Você também pode gastar tempo avaliando a composição da memória do seu sistema e dos aplicativos principais e seu comportamento sob recuperação moderada de memória.

Ao falar sobre vm.swappiness, uma mudança extremamente importante a considerar em relação aos tempos recentes é essa mudança para vmscan por Satory Moriya em 2012, que muda vm.swappiness=0 significativamente como isso é tratado.

Essencialmente, o patch faz com que sejamos extremamente tendenciosos contra a varredura (e, portanto, a recuperação) de qualquer página anônima com vm.swappiness = 0, a menos que já estejamos enfrentando uma grave contenção de memória. Conforme mencionado anteriormente nessa postagem, geralmente não é isso que você deseja, pois evita a igualdade de recuperação antes da ocorrência de pressão extrema de memória, o que pode, na verdade é levar a essa pressão extrema de memória em primeiro lugar. vm.swappiness = 1 é o nível mais baixo que você pode atingir sem invocar a caixa especial para verificação anônima de páginaas implementada nesse patch.

O padrão do kernel aqui é vm.swappiness = 60. Geralmente, esse valor não é tão ruim para a maioria das cargas de trabalho, mas é difícil ter um padrão geral adequado a todas as cargas de trabalho. Como tal, uma extensão valiosa para o ajuste mencionado na seção “quanta troca eu preciso” acima seria testar esses sistemas com valores diferentes para vm.swappiness e monitorar as métricas do seu aplicativo e do sistema sob carga pesada (de memória). Em algum momento no futuro próximo, quando tivermos uma implementação decente de detecção de refault no kernel, você também será capaz de determinar essa carga de trabalho de forma um tanto agnóstica, observando as métricas de refaulting de página no cgroup v2.

Fim da Tradução


Conclusão

Este tipo de conhecimento é muito importante na área de Osdev principalmente aqueles iniciantes que buscam construir seu próprio SO. No meu caso, eu tenho o sonho de construir meu kernel Post sobre o Fkernel havia lido este post em um outro posts Optimizing linux for slow computers e ele estava numa sessão de comentários então meu lado curioso foi pesquisar.

Pórem, todavia, entretanto é sempre bom salientar que este é blog post de 2 de Janeiro de 2018 e muitas coisas sobre implementação possam ter sido mudadas de lá para cá. É importante ler com uma pitada de sal sempre verificando se houve atualizações nessas lógicas. Porém toda a parte teórica do swap, para que ele serve e outros ademais deve ser retido.

Espero que esta tradução tenha sido útil para você

comments powered by Disqus
Tags: