Qual é o significado de acessos à memória “não temporais” em x86

123

Esta é uma questão de baixo nível. Na montagem x86, existem duas instruções SSE:

MOVDQA xmmi, m128

e

MOVNTDQA xmmi, m128

O Manual do Desenvolvedor de Software IA-32 diz que NT em MOVNTDQA significa Non-Temporal e que, caso contrário, é igual a MOVDQA.

Minha pergunta é: o que significa não temporal ?

Nathan Fellman
fonte
6
Observe que SSE4.1 MOVNTDQA xmmi, m128é uma carga NT, enquanto todas as outras instruções NT são armazenadas, exceto para prefetchnta. A resposta aceita aqui parece ser apenas sobre lojas. Isso é o que descobri sobre cargas do NT . TL: DR: espero que a CPU faça algo útil com a dica do NT para minimizar a poluição do cache, mas eles não sobrescrevem a semântica fortemente ordenada da memória WB "normal", então eles têm que usar o cache.
Peter Cordes
5
Atualização: as cargas do NT podem não fazer nada de útil, exceto nas regiões de memória UCSW na maioria das CPUs (por exemplo, família SnB Intel). No entanto, as lojas NT / streaming definitivamente funcionam na memória normal.
Peter Cordes
4
@ Pedro: Você quer dizer memória USWC, certo? Nunca ouvi falar de memória UCSW ou USWC antes. Pesquisar a sigla errada no Google não ajudou :-)
Andrew Bainbridge
4
@AndrewBainbridge: Sim, o atributo do tipo de memória WC. Combinação de gravação especulativa não armazenável em cache. Acho que estava colocando UnCacheable em maiúscula e lembrando que deveria ter 4 letras. : P
Peter Cordes

Respostas:

147

As instruções SSE não temporais (MOVNTI, MOVNTQ, etc.) não seguem as regras normais de coerência de cache. Portanto, os armazenamentos não temporais devem ser seguidos por uma instrução SFENCE para que seus resultados sejam vistos por outros processadores em tempo hábil.

Quando os dados são produzidos e não (imediatamente) consumidos novamente, o fato de que as operações de armazenamento de memória leem primeiro uma linha de cache completa e, em seguida, modificam os dados em cache é prejudicial ao desempenho. Esta operação remove os dados dos caches que podem ser necessários novamente em favor dos dados que não serão usados ​​em breve. Isso é especialmente verdadeiro para grandes estruturas de dados, como matrizes, que são preenchidas e usadas posteriormente. Antes que o último elemento da matriz seja preenchido, o tamanho total despeja os primeiros elementos, tornando o armazenamento em cache das gravações ineficaz.

Para esta e outras situações semelhantes, os processadores fornecem suporte para operações de gravação não temporais. Atemporal neste contexto significa que os dados não serão reutilizados em breve, portanto, não há razão para armazená-los em cache. Essas operações de gravação não temporais não leem uma linha de cache e a modificam; em vez disso, o novo conteúdo é gravado diretamente na memória.

Fonte: http://lwn.net/Articles/255364/

Espo
fonte
15
Boa resposta, gostaria apenas de salientar que no tipo de processador com instruções NT, mesmo com uma instrução atemporal (ou seja, uma instrução normal), o cache de linha não é "lido e modificado". Para uma instrução normal gravando em uma linha que não está no cache, uma linha é reservada no cache e uma máscara indica quais partes da linha estão atualizadas. Esta página da web o chama de "no stall on store": ptlsim.org/Documentation/html/node30.html . Não consegui encontrar referências mais precisas, só ouvi falar de caras cujo trabalho é implementar simuladores de processador.
Pascal Cuoq
2
Na verdade, ptlsim.org é um site sobre um simulador de processador com precisão de ciclo, exatamente o mesmo tipo de coisa que os caras que me falaram sobre "sem estol na loja" estão fazendo. É melhor eu mencioná-los também, caso eles vejam este comentário: unisim.org
Pascal Cuoq
1
Pelas respostas e comentários aqui stackoverflow.com/questions/44864033/… parece que SFENCEpode não ser necessário. Pelo menos no mesmo tópico. Você também poderia olhar?
Serge Rogatch
1
@SergeRogatch depende de qual cenário você está falando, mas sim, existem cenários em que sfenceé necessário para armazenamentos NT, ao passo que nunca é necessário apenas para armazenamentos normais. As lojas NT não são encomendadas em relação a outras lojas (NT ou não), como visto por outras threads , sem um sfence. Para leituras do mesmo encadeamento que fez os armazenamentos, entretanto, você nunca precisa sfence: um determinado encadeamento sempre verá seus próprios armazenamentos na ordem do programa, independentemente de serem ou não armazenamentos do NT.
BeeOnRope
40

Espo acerta o alvo. Só queria adicionar meus dois centavos:

A frase "atemporal" significa ausência de localidade temporal. Os caches exploram dois tipos de localidade - espacial e temporal, e ao usar uma instrução atemporal você está sinalizando ao processador que não espera que o item de dados seja usado em um futuro próximo.

Estou um pouco cético quanto ao assembly codificado manualmente que usa as instruções de controle de cache. Em minha experiência, essas coisas levam a mais bugs do que qualquer aumento efetivo de desempenho.

Pramod
fonte
pergunta sobre "assembly codificado à mão que usa as instruções de controle de cache." Eu sei que você disse explicitamente "codificado à mão" sobre algo como um JavaVM. Este é um caso de uso melhor? O JavaVM / Compiler analisou o comportamento estático e dinâmico do programa e usa essas instruções não temporais.
Pat
4
Explorar propriedades de localidade conhecidas (ou a falta delas) de seu domínio de problema, algoritmo ou aplicativo não deve ser evitado. Evitar a poluição do cache é de fato uma tarefa de otimização muito atraente e eficaz. Além disso, por que a aversão à assembléia? Existem muitas oportunidades de ganhos disponíveis que um compilador não pode capitalizar
awdz9nld
5
É definitivamente verdade que um programador experiente de baixo nível pode superar um compilador para pequenos kernels. Isso é ótimo para publicar artigos e postagens de blog, e eu fiz os dois. Eles também são boas ferramentas didáticas e ajudam a entender o que "realmente" está acontecendo. Porém, em minha experiência, na prática, onde você tem um sistema real com muitos programadores trabalhando nele e a correção e a facilidade de manutenção são importantes, o benefício da codificação de baixo nível é quase sempre superado pelos riscos.
Pramod
4
@Pramod esse mesmo argumento facilmente generaliza para a otimização em geral e não está realmente no escopo da discussão - claramente que o trade-off já foi considerado ou de outra forma foi considerado irrelevante, dado o fato de que já estamos falando sobre instruções atemporais
awdz9nld
7

De acordo com o Manual do desenvolvedor de software das arquiteturas Intel® 64 e IA-32, Volume 1: Arquitetura básica, capítulo "Programação com extensões Intel Streaming SIMD (Intel SSE)":

Cache de dados temporais vs. não temporais

Os dados referenciados por um programa podem ser temporais (os dados serão usados ​​novamente) ou atemporais (os dados serão referenciados uma vez e não reutilizados no futuro imediato). Por exemplo, o código do programa é geralmente temporal, enquanto os dados de multimídia, como a lista de exibição em um aplicativo gráfico 3-D, costumam ser atemporais. Para fazer uso eficiente dos caches do processador, geralmente é desejável armazenar em cache os dados temporais e não os dados não temporais. Sobrecarregar os caches do processador com dados não temporais é algumas vezes referido como "poluir os caches". As instruções de controle de capacidade de cache SSE e SSE2 permitem que um programa grave dados não temporais na memória de uma maneira que minimiza a poluição de caches.

Descrição das instruções de carregamento e armazenamento não temporais. Fonte: Intel 64 and IA-32 Architectures Software Developer's Manual, Volume 2: Instruction Set Reference

CARGA (MOVNTDQA - Carregar Dica de Alinhamento Não Temporal de Duas Palavras Quadword)

Carrega uma palavra quádrupla dupla do operando de origem (segundo operando) para o operando de destino (primeiro operando) usando uma dica não temporal se a fonte de memória for do tipo de memória WC (combinação de gravação) [...]

[...] o processador não lê os dados na hierarquia de cache, nem busca a linha de cache correspondente da memória na hierarquia de cache.

Observe que, como Peter Cordes comenta, não é útil na memória WB (write-back) normal nos processadores atuais porque a dica do NT é ignorada (provavelmente porque não há pré-buscadores HW compatíveis com o NT) e a semântica de carga totalmente ordenada se aplica . prefetchntapode ser usado como uma carga de redução de poluição da memória WB

STORE (MOVNTDQ - Armazenar Inteiros Empacotados Usando Dica Não Temporal)

Move os inteiros compactados no operando de origem (segundo operando) para o operando de destino (primeiro operando) usando uma dica não temporal para evitar o armazenamento em cache dos dados durante a gravação na memória.

[...] o processador não grava os dados na hierarquia de cache, nem busca a linha de cache correspondente da memória para a hierarquia de cache.

Usando a terminologia definida em Políticas e desempenho de gravação de cache , eles podem ser considerados como write-around (no-write-alocate, no-fetch-on-write-miss).

Finalmente, pode ser interessante revisar as notas de John McAlpin sobre armazenamentos não temporais .

chus
fonte
3
SSE4.1 MOVNTDQAsó faz algo especial em regiões de memória WC (Unacheable Write-Combining), por exemplo, RAM de vídeo. Não é de todo útil na memória WB (write-back) normal no HW atual, a dica do NT é ignorada e a semântica de carga totalmente ordenada é aplicada. prefetchntapode ser útil, entretanto, como uma carga de redução de poluição da memória WB. As arquiteturas x86 atuais suportam cargas não temporais (da memória "normal")? .
Peter Cordes
2
Isso é correto, os armazenamentos do NT funcionam bem na memória WB, são mal ordenados e geralmente são uma boa escolha para gravar grandes regiões da memória. Mas as cargas do NT não são. O manual do x86 no papel permite que a dica do NT faça algo para cargas da memória WB, mas nas CPUs atuais isso não faz nada . (Provavelmente porque não há pré-buscadores HW compatíveis com o NT.)
Peter Cordes
Eu adicionei essa informação relevante para a resposta. Muito obrigado.
chus
1
@LewisKelsey: os armazenamentos do NT substituem o tipo de memória. É por isso que eles podem ser solicitados de maneira fraca na memória WB. O principal efeito é evitar RFOs (aparentemente eles enviam um invalidate que limpa até mesmo outras linhas sujas quando chegam a mem). Eles também podem se tornar visíveis fora de ordem, de modo que não precisam esperar até que um armazenamento de cache-miss (regular) anterior seja confirmado ou até que um carregamento de cache-miss anterior obtenha dados. ou seja, o tipo de gargalo questionado em A memória fora de cada núcleo é sempre conceitualmente plana / uniforme / síncrona em um sistema multiprocessador? .
Peter Cordes
1
@LewisKelsey: Uma limpeza da máquina de pedidos de memória pode matar qualquer carga após um armazenamento de UC que não deveria ter sido feito antes, se necessário. Fora isso, o pedido de confirmação não entra em jogo até que a loja seja retirada do back-end fora de ordem. Isso não pode acontecer até que o uop do endereço de armazenamento seja executado, momento em que o tipo de memória para o endereço pode ser verificado. Um uop de endereço de armazenamento verifica o TLB quando ele é executado; é assim que as CPUs podem detectar falhas nos armazenamentos antes de se aposentarem. Ele não pode esperar até que a entrada SB esteja pronta para se comprometer com L1d; nesse ponto, a execução já passou.
Peter Cordes