Está atualizando a operação dupla atômica

10

Em Java, a atualização da variável double e long pode não ser atômica, já que double / long estão sendo tratados como duas variáveis separadas de 32 bits.

link

Em C ++, se estou usando o compilador Intel® Processor + Microsoft Visual C ++ de 32 bits, está atualizando a operação double (8 bytes) atomic?

Não consigo encontrar muita menção de especificação sobre esse comportamento.

Quando digo "variável atômica", aqui está o que quero dizer:

Thread A tentando escrever 1 na variável x. Tópico B tentando escrever 2 na variável x.

Devemos obter o valor 1 ou 2 da variável x, mas não um valor indefinido.

    
por Cheok Yan Cheng 18.08.2009 в 11:14
fonte

5 respostas

8

Isto é específico do hardware e depende da arquitetura. Para x86 e x86_64, é garantido que as gravações ou leituras de 8 bytes sejam atômicas, se estiverem alinhadas. Citando o white paper sobre pedidos de memória da Arquitetura Intel:

  

Garantias de pedido de memória Intel 64   que para cada um dos seguintes   instruções de acesso à memória, o   operação de memória constituinte aparece   para executar como um único acesso à memória   independentemente do tipo de memória:

     
  1. Instruções que leem ou escrevem um único byte.

  2.   
  3. Instruções que leem ou escrevem uma palavra (2 bytes) cujo endereço é   alinhado em um limite de 2 bytes.

  4.   
  5. Instruções que lêem ou escrevem uma palavra dupla (4 bytes) cujo endereço é   alinhado em um limite de 4 bytes.

  6.   
  7. Instruções que leem ou escrevem uma quadword (8 bytes) cujo endereço é   alinhado em um limite de 8 bytes.
  8.   

Todas as instruções bloqueadas (o implicitamente   instrução Xchg bloqueado e outros   leia-modifique-escreva instruções com um   prefixo de bloqueio) são indivisíveis e   sequência ininterrupta de carga (s)   seguido por loja (s), independentemente de   tipo de memória e alinhamento.

    
por Gunther Piez 18.08.2009 / 11:58
fonte
2

É seguro assumir que atualizar um duplo nunca é atômico, mesmo que seu tamanho seja o mesmo que um int com garantia atômica. A razão é que se tem um caminho de processamento diferente, pois é um tipo de dados não crítico e caro. Por exemplo, até mesmo as barreiras de dados geralmente mencionam que elas não se aplicam a dados / operações de ponto flutuante em geral.

O Visual C ++ unirá tipos primitivos (consulte artigo ) e enquanto isso deve garantir que seus bits não ficarão distorcidos enquanto estiver gravando na memória (o alinhamento de 8 bytes está sempre em uma linha de cache de 64 ou 128 bits) o restante depende de como o CPU lida com dados não-atômicos em seu cache e se lê / flui uma linha de cache é interruptiva. Então, se você pesquisar os documentos da Intel sobre o tipo de núcleo que está usando e lhe der essa garantia, então você está seguro para ir.

A razão pela qual o Java spec é tão conservador é que ele deve rodar da mesma maneira em um antigo 386 e no Corei7. O que é claro delirante, mas uma promessa é uma promessa, portanto promete menos :-)

A razão pela qual estou dizendo que você tem que procurar por um processador é que seu processador pode ser um antigo 386, ou algo semelhante :-)) Não esqueça que em um processador de 32 bits seu bloco de 8 bytes 2 "rodadas" para acessar, então você está à mercê da mecânica do acesso ao cache.

A limpeza da linha de cache, que oferece uma garantia de consistência de dados muito maior, aplica-se apenas a uma CPU razoavelmente recente com garantia da Intel (consistência automática de cache).

    
por ZXX 03.11.2010 / 02:45
fonte
-2

Eu não acho que em qualquer arquitetura, a comutação thread / context interromperia a atualização de um registrador até a metade para que você ficasse com 18bits atualizados dos 32bits que ele iria atualizar. O mesmo para atualizar um local de memória (desde que seja uma unidade básica de acesso, 8,16,32,64 bits, etc.).

    
por Indy9000 18.08.2009 / 11:32
fonte
-2

Então essa pergunta foi respondida? Eu corri um programa de teste simples, alterando um duplo:

#include <stdio.h>

int main(int argc, char** argv)
{
    double i = 3.14159265358979323;
    i += 84626.433;
}

Compilei-o sem otimizações (gcc -O0) e todas as operações de atribuição são executadas com instruções de um único assembler, como fldl .LC0 e faddp %st, %st(1) . ( i += 84626.433 é claro feito duas operações, faddp e fstpl ).

Um thread pode realmente ser interrompido dentro de uma única instrução, como faddp ?

    
por user287010 18.03.2010 / 10:47
fonte
-2

Em um multicore, além de ser atômico, você precisa se preocupar com a coerência do cache, para que a leitura do thread veja o novo valor em seu cache quando o gravador for atualizado.

    
por excalibur 06.01.2012 / 14:56
fonte