Aardvark fêmea com filhote. Foto: Scotto Bear , CC BY-SA 2.0Você escreve um programa para processamento de dados, ele passa no teste perfeitamente em um arquivo pequeno, mas trava com uma carga real.
O problema está sem memória. Se você tiver 16 gigabytes de RAM, não poderá baixar um arquivo de cem gigabytes lá. Em algum momento, o sistema operacional ficará sem memória, não poderá alocar um novo e o programa falhará.
O que fazer
Bem, você pode implantar um cluster de Big Data, apenas:
- Encontre um cluster de computadores.
- Configure em uma semana.
- Aprenda a nova API e reescreva seu código.
É caro e desagradável. Felizmente, muitas vezes não é necessário.
Precisamos de uma solução simples e fácil: processar dados em um computador, com configuração mínima e uso máximo de bibliotecas já conectadas. Isso quase sempre é possível com a ajuda de métodos mais simples, às vezes chamados de computação fora do núcleo.
Neste artigo, discutimos:
- Por que precisamos de RAM?
- A maneira mais fácil de processar dados que não cabem na memória é gastar um pouco de dinheiro.
- Três métodos principais de software para processar quantidades excessivas de dados: compactação, bloqueio e indexação.
Os artigos futuros mostrarão na prática como aplicar esses métodos com bibliotecas específicas, como NumPy e Pandas. Mas primeiro, a teoria.
Por que a RAM é necessária?
Antes de entrarmos na discussão de soluções, vamos esclarecer por que esse problema existe. Você pode gravar dados na memória de acesso aleatório (RAM), mas também no disco rígido. Por que você precisa de RAM? Um disco é mais barato, geralmente não tem problemas com falta de espaço; por que não se limitar apenas à leitura e gravação de um disco?
Teoricamente, isso pode funcionar. Mas mesmo os SSDs rápidos modernos funcionam
muito, muito mais lentamente que a RAM:
- Leia a partir do SSD: ~ 16.000 nanossegundos
- Leia a partir da RAM: ~ 100 nanossegundos
Para cálculos rápidos, não temos escolha: os dados devem ser gravados na RAM, caso contrário, o código diminuirá 150 vezes.
A solução mais fácil: mais RAM
A solução mais fácil para o problema de ficar sem memória RAM é gastar algum dinheiro. Você pode comprar um computador, servidor ou alugar uma máquina virtual com muita memória. Em novembro de 2019, uma pesquisa rápida e uma breve comparação de preços oferecem as seguintes opções:
- Compre o Thinkpad M720 Tower com 6 núcleos e 64 GB de RAM por US $ 1074
- Alugue uma máquina virtual na nuvem com 64 núcleos e 432 GB de RAM por US $ 3,62 / hora
Estes são apenas números após uma pesquisa rápida. Depois de fazer uma boa pesquisa, você certamente encontrará melhores ofertas.
Gastar um pouco de dinheiro em hardware para ajustar os dados na RAM geralmente é a solução mais barata. Afinal, nosso tempo é caro. Mas às vezes isso não é suficiente.
Por exemplo, se você executar muitas tarefas de processamento de dados por um período, a computação em nuvem pode ser uma solução natural, mas também pode ser cara. Em um de nossos projetos, esses custos de computação consumiriam toda a receita projetada do produto, incluindo a receita mais importante necessária para pagar meu salário.
Se comprar / alugar uma grande quantidade de RAM não resolver o problema ou não for possível, o próximo passo é otimizar o próprio aplicativo para que consuma menos memória.Técnica número 1. Compressão
A compactação permite colocar os mesmos dados em menos memória. Existem duas formas de compactação:
- Sem perdas : após a compactação, exatamente as mesmas informações são salvas como nos dados originais.
- Perda : os dados armazenados perdem alguns detalhes, mas, idealmente, isso não afeta muito os resultados do cálculo.
Apenas para maior clareza, não se trata de arquivos zip ou gzip quando os dados são compactados
no disco . Para processar dados de um arquivo ZIP, você normalmente precisa descompactá-lo e, em seguida, carregar os arquivos na memória. Então isso não vai ajudar.
O que precisamos é de uma compressão da representação dos dados na memória .Suponha que seus dados armazenem apenas dois valores possíveis, e nada mais:
"AVAILABLE"
e
"UNAVAILABLE"
. Em vez de armazenar seqüências de caracteres com 10 bytes ou mais por registro, você pode salvá-las como valores booleanos
True
ou
False
, que são codificados com apenas um byte. Você pode compactar informações até um bit, reduzindo o consumo de memória em outras oito vezes.
Técnica nº 2. Dividindo em blocos, carregando dados um bloco de cada vez
A fragmentação é útil em situações em que os dados não precisam ser carregados na memória ao mesmo tempo. Em vez disso, podemos carregá-los em partes, processando um fragmento de cada vez (ou, como discutiremos no próximo artigo, várias partes em paralelo).
Suponha que você queira encontrar a maior palavra em um livro. Você pode carregar todos os dados na memória de uma vez:
largest_word = "" for word in book.get_text().split(): if len(word) > len(largest_word): largest_word = word
Mas se o livro não couber na memória, você pode carregá-lo página por página:
largest_word = "" for page in book.iterpages(): for word in page.get_text().split(): if len(word) > len(largest_word): largest_word = word
Isso reduz bastante o consumo de memória, pois apenas uma página de um livro é carregada por vez. Nesse caso, o resultado será a mesma resposta.
Técnica nº 3. Indexação quando apenas um subconjunto de dados é necessário
A indexação é útil se você deseja usar apenas um subconjunto dos dados e carregar diferentes subconjuntos em momentos diferentes.
Em princípio, em tal situação, você pode filtrar a parte necessária e descartar o desnecessário. Mas a filtragem é lenta e não é ideal, porque você precisa primeiro carregar muitos dados extras na memória antes de soltá-los.
Se você precisar apenas de uma parte dos dados, em vez da fragmentação, é melhor usar um índice - um aperto de dados que indica sua localização real.Imagine que você queira ler apenas fragmentos de um livro mencionando aardvark (um mamífero fofo na fotografia no início do artigo). Se você verificar todas as páginas, o livro inteiro será carregado em partes, página por página, em busca de aardvarks - e isso levará bastante tempo.
Ou você pode abrir imediatamente o índice alfabético no final do livro - e encontrar a palavra "aardvark". Ele afirma que a palavra é mencionada nas páginas 7, 19 e 120-123. Agora você pode ler essas páginas, e somente elas, o que é muito mais rápido.
Esse é um método eficaz, pois o índice é muito menor que o livro inteiro, portanto, é muito mais fácil carregar apenas o índice na memória para encontrar os dados relevantes.
O método de indexação mais fácil
A maneira mais fácil e comum de indexar é nomear arquivos em um diretório:
mydata/ 2019-Jan.csv 2019-Feb.csv 2019-Mar.csv 2019-Apr.csv ...
Se você precisar de dados para março de 2019, basta fazer o upload do arquivo
2019-Mar.csv
- não há necessidade de baixar dados para fevereiro, julho ou qualquer outro mês.
Próximo: aplicando esses métodos
O problema da falta de RAM é mais fácil de resolver com a ajuda do dinheiro, tendo comprado a RAM. Mas se isso não for possível ou insuficiente, você usará a compactação, fragmentação ou indexação de qualquer maneira.
Os mesmos métodos são usados em vários pacotes e ferramentas de software . Até sistemas de Big Data de alto desempenho são criados sobre eles: por exemplo, processamento paralelo de fragmentos de dados individuais.
Nos artigos a seguir, veremos como aplicar esses métodos em bibliotecas e ferramentas específicas, incluindo NumPy e Pandas.