Pergunta

Esta é uma pergunta complexa, considere cuidadosamente antes de responder.

Considere esta situação.Dois threads (um leitor e um escritor) acessam um único global int.Isso é seguro?Normalmente, eu responderia sem pensar, sim!

No entanto, parece-me que Herb Sutter não pensa assim.Em seus artigos sobre concorrência efetiva ele discute uma fila sem bloqueio defeituosa e a versão corrigida.

No final do primeiro artigo e no início do segundo ele discute uma característica raramente considerada das variáveis, a ordenação da escrita.Os ints são atômicos, bons, mas os ints não são necessariamente ordenados, o que poderia destruir qualquer algoritmo sem bloqueio, incluindo meu cenário acima.Concordo plenamente que a única forma de garantia O comportamento multithread correto em todas as plataformas presentes e futuras é usar atômicos (também conhecidos como barreiras de memória) ou mutexes.

Minha pergunta;a reordenação de gravação é um problema em hardware real?Ou a paranóia multithread é apenas pedante?
E quanto aos sistemas uniprocessadores clássicos?
E quanto aos processadores RISC mais simples, como um power-pc integrado?

Esclarecimento:Estou mais interessado no que o Sr.Sutter disse sobre as gravações de variáveis ​​de reordenação de hardware (processador/cache).Posso impedir que o otimizador quebre o código com opções de compilador ou inspeção manual do assembly pós-compilação.Porém, gostaria de saber se o hardware ainda pode atrapalhar o código na prática.

Foi útil?

Solução

Sua ideia de inspecionar a montagem não é boa o suficiente; A reordenação pode acontecer no nível do hardware.

Para responder à sua pergunta "Este é um problema no hardware de leitura:" Sim! Na verdade, eu mesmo encontrei esse problema.

É bom contornar o problema com sistemas uniprocessadores ou outras situações de caso especial? Eu argumentaria "não" porque daqui a cinco anos você pode precisar correr com vários núcleos, e depois encontrar todos esses locais será complicado (impossível?).

Uma exceção: software projetado para aplicativos de hardware incorporados, onde você realmente controla completamente o hardware. Na verdade, eu "trapaceei" assim nessas situações em um processador de braço.

Outras dicas

YUP - Use barreiras de memória para evitar a reordenação de instruções quando necessário. Em alguns compiladores C ++, a palavra -chave volátil foi expandida para inserir barreiras implícitas de memória para cada leitura e gravação - mas essa não é uma solução portátil. (Da mesma forma com as APIs intertravadas* Win32). O Vista ainda adiciona algumas novas APIs de bloqueio de granulação mais fina que permitem especificar semântica de leitura ou gravação.

Infelizmente, o C ++ possui um modelo de memória tão frouxo que qualquer tipo de código como esse será não-portável até certo ponto e você terá que escrever versões diferentes para plataformas diferentes.

Como você disse, por causa da reordenação feita no nível do cache ou do processador, você realmente precisa de algum tipo de barreira de memória para garantir a sincronização adequada, especialmente para multi-processadores (e especialmente em plataformas não x86). (Sou dado a acreditar que os sistemas de processador único não têm esses problemas, mas não me citei sobre isso-eu certamente estou mais inclinado a jogar seguro e fazer o acesso sincronizado de qualquer maneira.)

Temos o problema, embora nos processadores Itanium, onde a reordenação de instruções é mais agressiva que o x86/x64.

A correção era usar uma instrução entrelaçada, pois havia (na época) não havia como dizer ao compilador para simplesmente apenas uma barreira de gravação após a tarefa.

Realmente precisamos de extensão de linguagem para lidar com isso de maneira limpa. O uso de volátil (se suportado pelo compilador) é muito grosso para os casos em que você está tentando espremer o máximo de desempenho possível com um pedaço de código.

isso é um problema em hardware real?

Com certeza, especialmente agora com a mudança para múltiplos núcleos para CPUs atuais e futuras.Se você depende da atomicidade ordenada para implementar recursos em seu aplicativo e não consegue garantir esse requisito por meio da plataforma escolhida ou do uso de primitivas de sincronização, em todos condições, ou seja,Se o cliente passar de uma CPU de núcleo único para uma CPU de vários núcleos, você estará apenas esperando que um problema ocorra.

Citando o referido artigo de Herb Sutter (segundo)

Variáveis ​​atômicas ordenadas são escritas de maneiras diferentes em plataformas e ambientes populares.Por exemplo:

  • volatile em C#/.NET, como em volatile int.
  • volatile ou *Atomic* em Java, como em volatile int, AtomicInteger.
  • atomic<T> em C++0x, o futuro padrão ISO C++, como em atomic<int>.

Não vi como o C++ 0x implementa a atomicidade ordenada, por isso não consigo especificar se o próximo recurso da linguagem é uma implementação pura da biblioteca ou também depende de alterações na linguagem.Você poderia revisar a proposta para ver se ela pode ser incorporada como uma extensão não padrão à sua cadeia de ferramentas atual até que o novo padrão esteja disponível; ele pode até já estar disponível para a sua situação.

É um problema no hardware real. Um amigo meu trabalha para a IBM e ganha a vida principalmente, abandondo esse tipo de problema nos códigos dos clientes.

Se você quiser ver como as coisas podem ficar ruins, pesquise artigos acadêmicos no modelo de memória Java (e também agora o modelo de memória C ++). Dada a reordenação que o hardware real pode fazer, tentando descobrir o que é seguro em um idioma de alto nível é um pesadelo.

Não, isso não é seguro e há hardware real que exibe esse problema, por exemplo, o modelo de memória no chip PowerPC no Xbox 360 permite que as gravações sejam reordenadas. Isso é exacerbado pela falta de barreiras na intrínseca, veja este artigo sobre msdn para mais detalhes.

A resposta para a pergunta "é segura" é inerentemente ambígua.

É sempre seguro, mesmo para duplas, no sentido de que seu computador não pega fogo. É seguro, no sentido de que você sempre terá um valor que o int mantido em algum momento no passado, não é seguro, no sentido de que você pode obter um valor que é/será atualizado por outro thread.

"Atomic" significa que você recebe a segunda garantia. Como o duplo geralmente não é atômico, você pode obter 32 velhos e 32 bits novos. Isso é claramente inseguro.

Quando fiz a pergunta, mais interessado no Uniprocessor PowerPC. Em um dos comentários Inscitek Jeff mencionou as instruções PowerPC Sync e ISYNC. Aqueles em que a chave para uma resposta definitiva. Eu encontrei aqui no site da IBM.

O artigo é grande e bastante denso, mas a retirada é não, não é seguro. Nos otimizadores de memória do PowerPC mais antigos, onde não são sofisticados o suficiente para causar problemas em um uniprocessador. No entanto, os mais novos são muito mais agressivos e podem quebrar o acesso simples a uma Int Global.

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