Este artigo estuda principalmente o conteúdo relevante do cache LRU de alta taxa de transferência e de threads, como segue.
Alguns anos atrás, implementei um cache da LRU para encontrar seu ID para palavras -chave. A estrutura de dados é muito interessante porque a taxa de transferência necessária é grande o suficiente para eliminar os problemas de desempenho causados pelo grande número de locks e palavras -chave synchronized . O aplicativo é implementado em Java.
Eu pensei que uma série de alocações de referência atômica manteria a ordem de LRUs e LRU no concorrente. No começo, envolvi o valor na entrada. A entrada possui um nó na cadeia LRU da lista de ligações duplas. A cauda da corrente mantém a entrada usada recentemente e o nó da cabeça armazena a entrada que pode ser limpa quando o cache atingir um determinado tamanho. Cada nó aponta para a entrada usada para encontrar.
Quando você procura o valor através da chave, o cache primeiro precisa procurar o mapa para ver se esse valor existe. Se não existir, dependerá do carregador para ler o valor da fonte de dados de uma maneira de leitura e adicioná-lo no mapa no "Adicionar se houver falta". O desafio de garantir a alta taxa de transferência é manter efetivamente a cadeia LRU. Este mapa simultâneo de hash é segmentado e o nível do encadeamento está em um determinado nível (você pode especificar o nível de simultaneidade quando construir o mapa) não experimentará muita competição de threads. Mas a cadeia LRU não pode ser dividida da mesma maneira? Para resolver esse problema, introduzi uma fila auxiliar para operações de compensação.
Existem seis métodos básicos em cache. Para acertos em cache, a pesquisa inclui duas operações básicas: obter e oferecer e, para perda grossa, existem quatro métodos básicos: obter, carregar, colocar e oferecer. No método put, podemos precisar rastrear a operação clara. Quando o cache acertar, fazemos uma limpeza passivamente na cadeia LRU chamada operação de purificação.
GET: entrada de pesquisa no mapa por chave
Carga: Valor de carregamento de uma fonte de dados
Coloque: Crie entrada e mapeá -la para a chave
OFERTA: Anexar um nó na lista da lista da LRU que se refere a uma entrada recentemente acessada
despejo: remova os nós na cabeça da lista e entradas associadas do mapa (depois que o cache atingir um determinado tamanho)
Purge: Exclua nós não utilizados na lista de LRU - nos referimos a esses nós como buracos, e a fila de limpeza mantém o controle desses
A operação de limpeza e a operação de purificação são grandes lotes de dados de processamento. Vamos dar uma olhada nos detalhes de cada operação.
A operação GET funciona da seguinte maneira:
GET (k) -> V Entrada de pesquisa por chave K Se o cache acertar, temos uma entrada e oferecer entrada e tente purgar alguns orifícios mais o valor de carga v para a chave k criar entrada e < - (k, v) tente colocar a entrada e o valor de retorno final ev
Se a chave existir, fornecemos um novo nó na cauda da cadeia LRU para indicar que esse é um valor usado recentemente. A execução de Get and Oferta não é uma operação atômica (sem bloqueio aqui), por isso não podemos dizer que isso ofereceu pontos de nó para a entidade usada recentemente, mas é definitivamente a entidade usada mais recentemente obtida quando executamos simultaneamente. Não forçamos a obtenção e oferecemos para executar a ordem entre os threads, pois isso pode limitar a taxa de transferência. Depois de oferecer um nó, tentamos fazer algumas operações para limpar e retornar o valor. Vamos dar uma olhada na oferta e purgar operações em detalhes abaixo.
Se ocorrer perda de cache, chamaremos o carregador para carregar o valor para esta chave, criar uma nova entidade e colocá -la no mapa, e a operação de put é a seguinte:
Put (e) -> E EXISTENTE EX < - MAP.PUPTIFABSENT (EK, E) Se ausente oferece a entrada e; Se o tamanho chegar ao limite de despejo de despejo, algumas entradas de retorno final entrada e else, temos uma entrada existente de entrada ex.
Como você pode ver, pode haver concorrência quando dois ou mais threads colocam uma entidade no mapa, mas apenas um sucesso é permitido e a oferta será chamada. Depois de fornecer um nó na cauda da cadeia LRU, precisamos verificar se o cache atingiu seu limite, que é o identificador que usamos para iniciar a operação clara do lote. Neste cenário de aplicação específico, a configuração do limite é menor que a capacidade. A operação de limpeza ocorre em um pequeno lote e não quando todas as entidades são adicionadas. Vários encadeamentos podem participar da operação de compensação até que a capacidade do cache atinja sua capacidade. O bloqueio é fácil, mas os threads podem ser seguros. A limpeza do nó da cabeça da cadeia LRU requer remoção, o que requer operações atômicas cuidadosas para evitar operações de remoção multithread no mapa.
Esta operação de oferta é muito interessante. Ele sempre tenta criar um nó, mas não tenta remover e excluir nós que não são mais usados no LRU imediatamente.
Oferta (e) Se o nó da cauda não se referir à entrada e atribuir o nó atual c <-pt criar um novo nó n (e), novos referentes de nós para a entrada e se atômico comparar e definir o nó pt, espere c, atribuir n adicione nó n ao cauda da lista de lRU, se o nó não é nulo.
Primeiro, verificará se o nó na cauda da cadeia não aponta para a entidade que foi acessada, o que não é diferente, a menos que todos os threads acessem frequentemente o mesmo par de valor-chave. Ele criará um novo nó na cauda da corrente. Quando essa entidade é diferente, antes de fornecer um novo nó, ela tenta fazer uma comparação e definir operação para a entidade, o que impedirá que vários threads façam a mesma coisa.
O encadeamento que aloca com sucesso os nós fornece um novo nó no final da cadeia LRU. Esta operação é a mesma que a descoberta no ConcurrentLinkedQueue. O algoritmo de dependência é descrito no artigo a seguir. Algoritmos de fila simultâneos, rápidos e práticos e não bloqueadores. O tópico verificará se a entidade está relacionada a outros nós antes. Nesse caso, o nó antigo não será excluído imediatamente, mas será marcado como um buraco (a referência à sua entidade será definida para ser esvaziada)
O exposto acima é toda a explicação detalhada deste artigo sobre o cache LRU de alto rendimento e seguro de linha, e espero que seja útil para todos. Amigos interessados podem continuar se referindo a outros tópicos relacionados neste site. Se houver alguma falha, deixe uma mensagem para apontá -la. Obrigado amigos pelo seu apoio para este site!