Melhorando a performance da sua aplicação com Memoization
Escrito em 26 de Outubro de 2021 por Redação iugu
Atualizado em 30 de Abril de 2024
Criar códigos para relatórios e processamentos complexos pode ser um processo exaustivo, com muitas repetições e códigos longos demais.
É para resolver essas questões que usamos o memoization, que nada mais é do que uma técnica de otimização do código, que pode acelerar a performance ao armazenar resultados em cache, e retornando-os quando requeridos.
Quer entender como aplicar o memoization aos seus projetos? Continue lendo este artigo e aprenda quando e como utilizar essa técnica.
O que é o Memoization?
Memoization é uma das formas de otimização do código para que o cache seja o primeiro resultado de uma função, e retorne posteriormente.
Essa técnica evita que um novo processamento seja feito repetidas vezes, aumentando a velocidade da ação.
Quando utilizar o Memoization?
O memoization pode ser utilizado para diferentes objetivos, sendo os principais:
- Queries demoradas;
- Processamentos complexos;
- Geração de relatórios de diversos formatos;
- Funções que nunca mudam o resultado quando recebem o mesmo parâmetro;
- Chamadas API remotas;
- Entre outros.
Porém, é preciso sempre analisar se o memoization é a melhor opção para otimizar o cache.
Como utilizar o Memoization?
Por ser uma técnica aplicada à linguagem de código, o memoization pode ser aplicado a diferentes tecnologias.
Para ilustrar alguns exemplos do uso do memoization, na prática, vamos utilizar a linguagem Ruby.
Exemplo de uso em queries
Vamos pensar em um cenário onde um usuário consiga ver a quantidade de posts criado por ele logo na página inicial, em um dashboard.
Assim que ele acessar a página, esta query já será executada e em alguma parte da página será exibido este resultado.
Imagine isto sendo exibido (por algum motivo) em dois, três ou mais lugares (na mesma requisição) e sem a otimização.
O problema também fica mais complicado se você usa um banco de dados pago por leitura (como o Firebase ou DynamoDB).
Além de economizar os custos do projeto, o memoization garante que a velocidade de resposta também seja otimizada.
Analise o exemplo abaixo de uma query sem otimização:
class User |
E como conseguimos otimizar esta chamada? Veja abaixo:
class User |
Com essa comparação, pode surgir a dúvida sobre o uso do cache ao invés do memoization.
Porém, esse método é mais assertivo, garantindo que não ocorra a invalidação de cache.
Exemplo em geração de relatórios
Além das queries, o memoization também pode ser utilizado para a geração de relatórios.
Utilizando o exemplo abaixo, com um relatório que utiliza a query anterior, é possível tirar algumas conclusões.
A classe utilizada será hipotética, para demonstrar o erro, na prática. Portanto, não use esse modelo para o seu código!
class Report # não é a melhor opção, pois a função será executada # duas vezes (ou mais, quando por método). |
E como resolvemos esse caso? Assim como no exemplo anterior, basta usar o memoization.
class Report |
Depois de um processamento demorado, é possível gerar um PDF ou um XLS, por exemplo, com a mesma fonte de dados e sem divergências.
Dicas para usar o Memoization
Até o momento vimos algo relativamente simples e, sim, podem existir casos mais complexos que tenham entrada de parâmetros.
Para demonstrar isso, vamos usar novamente a classe User, mas com um novo desafio: utilizar um método com parâmetro.
class User |
A função acima pode parecer completa, mas não está!
Vamos relembrar que o memoization é uma técnica de otimização que, basicamente, faz cache do primeiro resultado de uma função e retorna isso posteriormente.
Portanto, se deixarmos a função como está, não vamos ter o resultado esperado.
Veja a seguir o problema e como podemos resolver.
user = User.find(id) |
Qual vai ser o resultado se chamarmos o método, passando o :title como parâmetro, depois que já tiver chamado o mesmo método que ordena pelo campo :created_at ?
user = User.find(id) |
Sabemos agora que o memoization usado de forma errada pode nos trazer problemas.
Então vamos para a solução, que pode parecer um pouco estranha (e talvez seja mesmo), mas resolve completamente este problema.
Para isso, vamos adicionar a variável field dentro de @posts_by def posts_order_by(field: :created_at).
class User |
Agora com o código certo, vejamos como ficaria o resultado.
user = User.find(id) |
Para entender por que a função está correta, acompanhe a explicação abaixo:
class User |
A partir disso, sabemos que, se adicionarmos um novo parâmetro, provavelmente teremos um novo problema.
Porém, há uma solução para que o memoization funcione em uma função com dois parâmetros.
class User |
Alterando bem pouco do código, já podemos ter uma versão funcional com mais de um parâmetro.
Resolução de nil em Memoization
Infelizmente nem tudo são flores. Temos ainda alguns “problemas” com o memoization e é exatamente quando ele executa algum processamento e retorna nil.
class User |
Caso seja preciso chamar o método last_post mais de uma vez (imaginando que nosso usuário não tenha nenhum post), o memoization não será eficiente.
Uma vez que ele vai procurar algum valor em @last_post e como está nil, ele vai executar a query todas as vezes que for chamado e é neste ponto que começamos a ter dor de cabeça com o memoization, pois temos que fazer mais checagens a fim de evitar esse problema.
Uma das soluções seria esta abaixo, mas é mais complexo de se tratar e cobrir os casos que podem retornar nil.
class User |
Mas claro, ninguém vai repetir isso em todas as funções. Nada que um helper, concern ou algo parecido não consiga resolver nesses casos.
Conclusão sobre o Memoization
O memoization pode ser uma ótima solução para otimizar códigos específicos, que podem ser repetitivos e longos demais. Porém, é preciso usá-lo com cautela.
Como vimos neste artigo, o memoization pode trazer alguns problemas quando usado de forma errada ou precitada.
O memoization também pode causar uma sobrecarga no armazenamento, para salvar os arquivos temporários, principalmente se usado com memória RAM.
Porém, a depender do projeto, o memoization é uma boa saída para economizar tempo e custos. Fica a seu critério (e das necessidades do projeto) usar ou não essa técnica.
Escrito em 26 de Outubro de 2021 por
Redação iugu