Pergunta

A outra semana, eu escrevi um pequeno classe Thread e um tubo de mensagem unidirecional para permitir a comunicação entre threads (dois tubos por thread, obviamente, para a comunicação bidirecional). Tudo funcionou bem no meu Athlon 64 X2, mas eu queria saber se eu tiver problemas se ambos os tópicos estavam olhando para a mesma variável eo valor em cache local para esta variável em cada núcleo estava fora de sincronia.

Eu sei que o volátil palavra-chave irá forçar uma variável para atualizar a partir da memória, mas há uma maneira em multicore x86 processadores para forçar os caches de todos os núcleos para sincronizar? Isto é algo que eu preciso para se preocupar, ou será volátil e uso adequado dos mecanismos de bloqueio leves (eu estava usando _InterlockedExchange para definir meus variáveis ??tubos voláteis) lidar com todos os casos onde eu quero escrever "lock livre" código para multicore x86 CPUs?

Eu já estou ciente e usaram seções críticas, Mutexes, eventos e assim por diante. Estou principalmente se perguntando se há x86 intrínsecos que eu não estou ciente de que a força ou pode ser usado para reforçar a coerência de cache.

Foi útil?

Solução

volatile únicas forças seu código para re-ler o valor, ele não pode controlar onde o valor é lido. Se o valor foi recentemente lido por seu código, em seguida, ele provavelmente será em cache, em caso volátil irá forçá-lo a ser o que re-ler a partir do cache, não da memória.

Não há um monte de instruções de coerência de cache em x86. Há instruções de pré-busca, como prefetchnta , mas isso não afeta a semântica memória de ordenação. É usado para ser implementado, trazendo o valor para cache L1 sem poluir L2, mas as coisas são mais complicadas para Intel moderno projeta com um grande compartilhada inclusiva cache L3.

CPUs x86 usar uma variação sobre o MESI protocolo (MESIF para a Intel, MOESI para AMD) para manter seus caches coerentes entre si (incluindo os caches L1 privadas de diferentes núcleos). Um núcleo que quer escrever uma linha de cache tem de forçar outros núcleos para invalidar a sua cópia do mesmo antes que possa mudar a sua própria cópia do Shared para estado modificado.


Você não precisa quaisquer instruções de vedação (como MFENCE) para dados de produzir um fio e consumi-lo em outro em x86, porque x 86 cargas / lojas têm semântica aquisição / liberação embutido. Você precisa MFENCE (barreira completa) para obter a consistência seqüencial. (A versão anterior desta resposta sugeriu que clflush era necessário, o que é incorreto).

Você precisa fazer para evitar tempo de compilação reordenação , porque modelo de memória de C ++ é fracamente-ordenada. volatile é uma maneira ruim de idade, para fazer isso; C ++ 11 std :: atômica é a melhor maneira muito para código sem bloqueio de gravação.

Outras dicas

coerência

cache é assegurada entre os núcleos devido ao protocolo mesi empregue por x86 processadores. Você só precisa se preocupar com a coerência de memória ao lidar com hardware externo que pode acessar a memória enquanto os dados ainda é situação em caches núcleos. Não parece que é o seu caso aqui, embora, desde que o texto sugere que você está programando em userland.

Você não precisa se preocupar com a coerência de cache. O hardware vai cuidar disso. O que você pode precisar se preocupar com problemas de desempenho devido a essa coerência de cache.

Se centrais # 1 gravações para uma variável, que invalida todas as outras cópias da linha de cache em outros núcleos (porque tem de obter propriedade exclusiva da linha de cache antes de cometer a loja). Quando o núcleo # 2 lê essa mesma variável, ele vai perder em cache (a menos núcleo # 1 já escreveu de volta, tanto quanto um nível compartilhada de cache).

Uma vez que uma linha de cache inteiro (64 bytes) tem de ser lido a partir da memória (ou de volta escrito para cache compartilhado e, em seguida, lido pelo núcleo # 2), ele terá algum custo desempenho. Neste caso, é inevitável. Este é o comportamento desejado.


O problema é que quando você tem múltiplas variáveis ??na mesma linha de cache, o processador pode gastar mais tempo mantendo as caches em sincronia, mesmo que os núcleos são leitura / escrita variáveis ??diferentes dentro da mesma linha de cache.

Esse custo pode ser evitado por ter certeza que essas variáveis ??não estão na mesma linha de cache. Este efeito é conhecido como falso compartilhamento desde que você está forçando os processadores para sincronizar os valores de objetos que não são realmente compartilhados entre threads.

vontade volátil não fazê-lo. Em C ++, volátil afeta apenas otimizações que compilador, como armazenar uma variável num registo em vez de memória, ou removê-lo por completo.

Você não especificou qual compilador você está usando, mas se você estiver no Windows, dê uma olhada em este artigo aqui . Também dê uma olhada nas s disponíveis ynchronization funções aqui . Você pode querer nota que, em volatile geral não é suficiente para fazer o que você quer fazer, mas sob VC 2005 e 2008, há semântica não-padrão adicionados a ele que adicionam barreiras de memória implícitas em torno de ler e escreve.

Se você quer que as coisas para ser portátil, você vai ter uma estrada muito mais difícil pela frente.

Há uma série de artigos explicando arquiteturas de memória modernos aqui , incluindo < a href = "http://duartes.org/gustavo/blog/post/intel-cpu-caches" rel = "nofollow noreferrer"> caches Intel Core2 e muitas mais modernos arquitetura tópicos.

Os artigos são muito legível e bem ilustrado. Divirta-se!

Existem várias sub-questões na sua pergunta, então eu vou responder-lhes com o melhor de meu conhecimento.

  1. Não há atualmente nenhuma maneira portátil de implementar interações livre-lock em C ++. A proposta resolve C ++ 0x isso através da introdução da biblioteca atomics.
  2. volátil não é garantida para fornecer atomicity em um multicore e sua implementação é específico do fornecedor.
  3. Na x86, você não precisa fazer nada de especial, exceto variáveis ??de declaração compartilhada como volátil para impedir que algumas otimizações do compilador que podem quebrar o código multithreaded. Volátil diz o compilador não para os valores de cache.
  4. Existem alguns algoritmos (Dekker, por exemplo) que não irá funcionar mesmo em um x86 com variáveis ??voláteis.
  5. Se você não sabe com certeza que passando o acesso aos dados entre threads é um grande gargalo de desempenho em seu programa, ficar longe de soluções de livre-lock. Use passagem de dados por valor ou fechaduras.

O seguinte é um bom artigo em referência ao uso de volatile w / rosca programas.

Volatile quase inútil para multi-threaded programação.

Herb Sutter parecia simplesmente sugerem que quaisquer duas variáveis ??devem residir em linhas de cache separados. Ele faz isso em sua fila em simultâneo com preenchimento entre seus cabelos e ponteiros de nó.

Edit: Se você estiver usando o compilador Intel ou GCC, você pode usar o builtins atômicas , que parecem fazer o seu melhor para antecipar o cache quando possível.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top