Pular para o conteúdo

Como usar o LRU Cache no Python

[

Caching em Python usando a estratégia LRU Cache

Existem muitas maneiras de alcançar aplicativos rápidos e responsivos. O caching é uma abordagem que, quando usada corretamente, torna as coisas muito mais rápidas, enquanto diminui a carga nos recursos de computação. O módulo functools do Python vem com o decorador @lru_cache, que permite que você faça cache do resultado de suas funções usando a estratégia Least Recently Used (LRU). Essa é uma técnica simples, porém poderosa, que você pode usar para aproveitar o poder do caching no seu código.

Caching e seus usos

Caching é uma técnica de otimização que você pode usar em seus aplicativos para manter dados recentes ou frequentemente usados em locais de memória que são mais rápidos ou mais baratos computacionalmente para acessar do que a fonte original.

Imagine que você esteja construindo um aplicativo de leitura de notícias que busca as últimas notícias de fontes diferentes. À medida que o usuário navega pela lista, seu aplicativo faz o download dos artigos e os exibe na tela.

O que aconteceria se o usuário decidisse mover repetidamente para frente e para trás entre alguns artigos de notícias? A menos que você estivesse fazendo cache dos dados, seu aplicativo teria que buscar o mesmo conteúdo todas as vezes! Isso deixaria o sistema do usuário lento e colocaria pressão extra no servidor que hospeda os artigos.

Uma abordagem melhor seria armazenar o conteúdo localmente após buscar cada artigo. Então, da próxima vez que o usuário decidisse abrir um artigo, seu aplicativo poderia abrir o conteúdo de uma cópia armazenada localmente em vez de voltar à fonte original. Na ciência da computação, essa técnica é chamada de caching.

Implementando um cache usando um dicionário Python

Você pode implementar uma solução de caching em Python usando um dicionário.

Continuando com o exemplo do aplicativo de leitura de notícias, em vez de acessar diretamente o servidor sempre que você precisar baixar um artigo, você pode verificar se tem o conteúdo em seu cache e voltar ao servidor apenas se não tiver. Você pode usar o URL do artigo como chave no dicionário para recuperar o conteúdo associado. Veja um exemplo de implementação:

cache = {}
def get_article_content(url):
if url in cache:
return cache[url]
else:
# Faz o download do conteúdo do artigo
content = download_content(url)
# Armazena o conteúdo no cache
cache[url] = content
return content

Dessa forma, você evita buscar o mesmo conteúdo várias vezes e melhora o desempenho do seu aplicativo.

Estratégias de caching

Existem várias estratégias de caching que você pode adotar, dependendo das necessidades do seu aplicativo. Algumas das estratégias mais comuns são:

  • Least Recently Used (LRU): mantém um cache limitado de itens e remove os menos usados quando atinge o limite definido.
  • First In, First Out (FIFO): mantém um cache limitado de itens e remove o mais antigo quando atinge o limite definido.
  • Least Frequently Used (LFU): mantém um cache limitado de itens e remove os menos frequentemente usados quando atinge o limite definido.
  • Time-based expiration: define um tempo de vida para os itens no cache e remove automaticamente os itens que expiraram.

A estratégia LRU é especialmente útil quando você tem um espaço de armazenamento limitado e quer garantir que os itens mais usados sejam mantidos no cache. É nesse ponto que o decorador @lru_cache do módulo functools entra em jogo.

Investigando a estratégia LRU Cache

A estratégia LRU Cache, ou Least Recently Used (Menos Recentemente Usado), é uma técnica de caching que remove automaticamente os itens menos usados do cache quando o limite é atingido. Isso significa que os itens mais usados serão mantidos no cache, enquanto os menos usados serão descartados para liberar espaço para novos itens.

A implementação da estratégia LRU Cache em Python é facilitada pelo decorador @lru_cache do módulo functools. Esse decorador cria e gerencia automaticamente um cache para a função decorada. Ele armazena os argumentos e retornos da função em um cache interno e, quando a função é chamada novamente com os mesmos argumentos, o valor é recuperado do cache em vez de ser recalculado.

Usando @lru_cache para implementar o cache LRU em Python

Vamos ver como usar o decorador @lru_cache para implementar o cache LRU em Python.

Suponha que você esteja escrevendo uma função para calcular o n-ésimo número de Fibonacci, que é uma sequência de números onde cada número é a soma dos dois números anteriores. Você pode usar o decorador @lru_cache para armazenar os valores já calculados e evitar recalculá-los.

Aqui está um exemplo de implementação:

from functools import lru_cache
@lru_cache(maxsize=128)
def fibonacci(n):
if n < 2:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)

Nesse exemplo, a função fibonacci() é decorada com @lru_cache(maxsize=128), o que significa que o cache pode armazenar até 128 valores calculados anteriormente. Assim, quando a função fibonacci() é chamada com um determinado valor de n, o decorador verifica se o valor já está no cache. Se estiver, o valor é retornado imediatamente; caso contrário, o valor é calculado e armazenado no cache para uso futuro.

Isso permite que você calcule números de Fibonacci repetidas vezes sem ter que recalcular todos os valores anteriores todas as vezes, resultando em uma execução mais eficiente e rápida.

Adicionando expiração ao cache

Às vezes, você pode querer que os itens no cache expirem após um determinado período de tempo para garantir que estejam sempre atualizados. Felizmente, você pode expandir a funcionalidade do decorador @lru_cache para adicionar essa capacidade ao seu cache LRU em Python.

Um exemplo de implementação é utilizar o módulo datetime para verificar a data e a hora em que cada item é adicionado ao cache e removê-los quando a expiração for atingida. Aqui está um exemplo de como você pode fazer isso:

from functools import lru_cache
from datetime import datetime, timedelta
@lru_cache(maxsize=128)
def get_cached_data(key):
if key in cache:
creation_time, data = cache[key]
if datetime.now() - creation_time > timedelta(minutes=30):
# O item expirou, remova-o do cache
del cache[key]
else:
return data
# Busca os dados e armazena-os no cache
creation_time = datetime.now()
data = fetch_data(key)
cache[key] = (creation_time, data)
return data

Nesse exemplo, cada item no cache é armazenado como uma tupla contendo a data e hora de criação e os dados reais. Quando a função get_cached_data() é chamada para um determinado key, o decorador @lru_cache verifica se o item está no cache e se a expiração já foi atingida. Se sim, o item é removido do cache; caso contrário, os dados são retornados.

Essa funcionalidade de expiração permite que você mantenha seu cache atualizado com os dados mais recentes e evite retornar dados desatualizados para os usuários.

Conclusão

Neste tutorial, você aprendeu como usar a estratégia de caching LRU Cache em Python para melhorar o desempenho do seu código. Exploramos diferentes estratégias de caching e vimos como implementar o cache LRU usando o decorador @lru_cache. Também discutimos como adicionar expiração ao cache para garantir que os dados estejam sempre atualizados.

Agora você tem uma compreensão mais profunda de como o caching funciona e como aproveitá-lo em Python. Experimente essas técnicas em seus próprios projetos e veja como elas podem melhorar a performance e a eficiência do seu código.