concurrency Java: CAS vs bloqueio

Eu estou lendo o Book Java Concurrency in Practice . No capítulo 15, eles estão falando sobre os algoritmos não-bloqueantes e o método compare-and-swap (CAS).

Está escrito que o CAS tem um desempenho muito melhor que os methods de bloqueio. Eu quero perguntar às pessoas que já trabalharam com esses dois conceitos e gostariam de saber quando você está preferindo qual desses conceitos? É realmente muito mais rápido?

Para mim, o uso de bloqueios é muito mais claro e mais fácil de entender e talvez até melhor de manter (por favor me corrija se eu estiver errado) . Deveríamos realmente nos concentrar em criar nosso código concorrente relacionado ao CAS do que bloqueios para obter um melhor aumento de desempenho ou a sustentabilidade é mais importante?

Eu sei que talvez não exista uma regra estrita quando usar o que. Mas eu gostaria de ouvir algumas opiniões, experiências com o novo conceito de CAS.

O CAS é geralmente muito mais rápido que o bloqueio, mas depende do grau de contenção. Como o CAS pode forçar uma nova tentativa se o valor mudar entre a leitura e a comparação, um encadeamento pode, teoricamente, ficar preso em uma espera ocupada se a variável em questão estiver sendo atingida por muitos outros encadeamentos (ou se for caro computar um novo valor do valor antigo (ou ambos)).

O principal problema com o CAS é que é muito mais difícil programar corretamente do que bloquear. Lembre-se, o bloqueio é, por sua vez, muito mais difícil de usar do que o envio de mensagens ou o STM , por isso não tome isso como um endosso para o uso de bloqueios.

A velocidade relativa das operações é em grande parte um não-problema. O que é relevante é a diferença na escalabilidade entre algoritmos baseados em bloqueio e não-bloqueantes. E se você estiver usando um sistema de 1 ou 2 núcleos, pare de pensar sobre essas coisas.

Geralmente, os algoritmos não bloqueadores são dimensionados melhor porque possuem “seções críticas” mais curtas do que os algoritmos baseados em bloqueio.

Você pode examinar os números entre um ConcurrentLinkedQueue e um BlockingQueue . O que você verá é que o CAS é visivelmente mais rápido em contenção moderada (mais realista em aplicações do mundo real).

A propriedade mais atraente dos algoritmos não-bloqueadores é o fato de que, se um encadeamento falhar (falta de cache, ou pior, falha de seg), outros encadeamentos não perceberão essa falha e poderão seguir em frente. No entanto, ao adquirir um bloqueio, se o encadeamento de retenção de bloqueio tiver algum tipo de falha do sistema operacional, todos os outros encadeamentos aguardando a liberação do bloqueio também serão afetados pela falha.

Para responder às suas perguntas, sim, algoritmos não bloqueantes de thread-safe ou collections ( ConcurrentLinkedQueue , ConcurrentSkipListMap/Set ) podem ser significativamente mais rápidos do que suas contrapartes de bloqueio. Como Marcelo apontou, obter algoritmos não-bloqueantes é muito difícil e requer muita consideração.

Você deve ler sobre a Fila Michael e Scott , esta é a implementação da fila para ConcurrentLinkedQueue e explica como lidar com uma function atômica bidirecional, segura para thread com um único CAS .

Há um bom livro fortemente relacionado ao tema da simultaneidade livre de bloqueio: “A arte da programação multiprocessador” por Maurice Herlihy

Se você estiver procurando por uma comparação do mundo real, aqui está uma. Nosso aplicativo possui dois (2) encadeamentos 1) Um encadeamento de leitor para captura de pacotes de rede e 2) um encadeamento de consumidor que pega o pacote, conta e relata statistics.

Thread 1 troca um único pacote de cada vez com o thread # 2

Resultado # 1 – usa uma troca baseada em CAS personalizada usando os mesmos princípios de SynchronousQueue , em que nossa class é chamada de CASSynchronousQueue :

 30,766,538 packets in 59.999 seconds :: 500.763Kpps, 1.115Gbps 0 drops libpcap statistics: recv=61,251,128, drop=0(0.0%), ifdrop=0 

Resultado # 2 – quando replacemos nossa implementação de CAS pelo padrão java SynchronousQueue :

 8,782,647 packets in 59.999 seconds :: 142.950Kpps, 324.957Mbps 0 drops libpcap statistics: recv=69,955,666, drop=52,369,516(74.9%), ifdrop=0 

Eu não acho que a diferença no desempenho não poderia ser mais clara.

Intereting Posts