O que o processador faz enquanto aguarda uma busca na memória principal


26

Supondo que as solicitações de cache l1 e l2 resultem em uma falha, o processador trava até que a memória principal seja acessada?

Ouvi falar da ideia de mudar para outro thread; se sim, o que é usado para ativar o thread parado?


4
Que pesquisa você fez? Esta é certamente a informação que está disponível. Vou deixar de responder aos especialistas, mas não acho que uma troca de threads seja útil. Geralmente, a alternância do contexto em uma CPU induz muitos acessos à memória (e, portanto, provavelmente o cache está em falta). Existem algumas medidas, como o reordenamento da operação (utilizando o pipeline), mas o bloqueio parece não ter alternativa.
Raphael

@ Rafael Eu acabei de ler livros sobre arquitetura de computadores, a arquitetura ARM System-on-Chip de Steve Furber, foi provavelmente a mais abrangente que já li completamente. No entanto, comecei a ler Arquitetura de Computadores: Uma Abordagem Quantitativa. Ele discute técnicas para evitar paralisações, como troca de threads, OOE e operações de memória fora de ordem, embora nunca dê muita atenção às complexidades dos projetos modernos, pois, como na maioria dos livros, eles cobrem arquiteturas mais antigas ou dão sugestões vagas sobre como essas coisas são. implementados e trabalhar juntos.
102948239408

Expandindo minha pergunta, os caches parecem ter latências pequenas e são deterministas em sua resposta, mas no caso de uma tabela de página de pior cenário caminhar para recuperar o endereço físico, milhares de instruções poderiam ser concluídas, algumas do mesmo segmento extraído pelo ILP. Quais interações de hardware ocorrem no processador para decidir que ele pode agendar outro encadeamento e qual comunicação é usada para ativar esse encadeamento, se isso acontecer. Ainda mais, se OoOE existe uma técnica para lidar com uma fila de resultados completa ao alternar threads?
102948239408

11
Não está claro em sua pergunta que você está interessado em detalhes de CPUs modernas. Provavelmente, isso não é apenas oftópico, mas também pode ser uma informação proprietária. Com os conceitos, podemos ajudá-lo; esses provavelmente mudaram menos ao longo das décadas do que as implementações. Quanto à sua pergunta, incorpore o que você conhece e formule uma pergunta conceitual específica (ou solicitação de referência).
Raphael

11
Eu respondi sobre os conceitos gerais, mas, a julgar pelos seus comentários, você pode estar após considerações mais avançadas. No entanto, se você quiser respostas mais avançadas, precisará tornar sua pergunta mais específica para arquiteturas e tipos de técnicas específicos.
Gilles 'SO- stop be evil'

Respostas:


28

A latência da memória é um dos problemas fundamentais estudados na pesquisa em arquitetura de computadores.

Execução especulativa

A execução especulativa com problema de instrução fora de ordem geralmente é capaz de encontrar trabalho útil para preencher a latência durante uma ocorrência de cache L1, mas geralmente fica sem trabalho útil após 10 ou 20 ciclos ou mais. Houve várias tentativas de aumentar a quantidade de trabalho que pode ser realizado durante uma falta de longa latência. Uma idéia era tentar fazer predição de valor (Lipasti, Wilkerson e Shen, (ASPLOS-VII): 138-147, 1996). Essa idéia estava muito na moda nos círculos de pesquisa da arquitetura acadêmica por um tempo, mas parece não funcionar na prática. Uma última tentativa de salvar a previsão de valor do caixote do lixo da história foi a execução de runahead(Mutlu, Stark, Wilkerson e Patt (HPCA-9): 129, 2003). Na execução com runahead, você reconhece que suas previsões de valor estarão erradas, mas especulativamente executa de qualquer maneira e depois lança todo o trabalho com base na previsão, na teoria de que você pelo menos iniciará alguns prefets para o que seria o cache L2 sente falta. Acontece que o runahead desperdiça tanta energia que simplesmente não vale a pena.

Uma abordagem final nesse sentido, que pode estar recebendo alguma tração na indústria, envolve a criação de buffers de reabastecimento enormemente longos. As instruções são executadas especulativamente com base na previsão de ramificação, mas nenhuma previsão de valor é feita. Em vez disso, todas as instruções que dependem de uma falha de carregamento de longa latência ficam sentadas e aguardam no buffer de reordenação. Mas como o buffer de reordenação é tão grande, você pode continuar buscando instruções se o preditor de ramificação estiver fazendo um trabalho decente, às vezes você poderá encontrar um trabalho útil muito mais tarde no fluxo de instruções. Um trabalho de pesquisa influente nessa área foram os dutos de fluxo contínuo(Srinivasan, Rajwar, Akkary, Gandhi e Upton (ASPLOS-XI): 107-119, 2004). (Apesar do fato de todos os autores serem da Intel, acredito que a ideia tenha mais força na AMD.)

Multi-rosqueamento

O uso de vários encadeamentos para tolerância à latência tem um histórico muito mais longo, com muito maior sucesso no setor. Todas as versões bem-sucedidas usam suporte de hardware para multithreading. A versão mais simples (e mais bem-sucedida) disso é o que costuma ser chamado de FGMT ( multi-threading de granulação fina ) ou multi-threading intercalado . Cada núcleo de hardware suporta vários contextos de encadeamento (um contexto é essencialmente o estado de registro, incluindo registros como o ponteiro de instruções e qualquer sinalizador implícito). Em um processador multithread de granulação fina, cada thread é processado em-ordem. O processador controla quais encadeamentos estão paralisados ​​em uma falta de carregamento de longa latência e quais estão prontos para a próxima instrução e usa uma estratégia simples de planejamento FIFO em cada ciclo para escolher qual encadeamento pronto para executar esse ciclo. Um exemplo inicial disso em larga escala foram os processadores HEP de Burton Smith (Burton Smith passou a arquitetar o supercomputador Tera, que também era um processador multithread de granulação fina). Mas a ideia remonta muito mais aos anos 1960, eu acho.

O FGMT é particularmente eficaz no fluxo de cargas de trabalho. Todas as GPUs modernas (unidades de processamento gráfico) são multicore, onde cada núcleo é FGMT, e o conceito também é amplamente usado em outros domínios de computação. O T1 da Sun também era FMGT multicore, assim como o Xeon Phi da Intel (o processador que ainda é chamado de "MIC" e costumava ser chamado de "Larabee").

A idéia de multithreading simultâneo (Tullsen, Eggers e Levy, (ISCA-22): 392-403, 1995) combina multithreading de hardware com execução especulativa. O processador possui vários contextos de encadeamento, mas cada encadeamento é executado especulativamente e fora de ordem. Um planejador mais sofisticado pode usar várias heurísticas para buscar no encadeamento que provavelmente terá um trabalho útil ( Malik, Agarwal, Dhar e Frank, (HPCA-14: 50-61), 2008 ). Uma certa grande empresa de semicondutores começou a usar o termo hyperthreading para multithreading simultâneo, e esse nome parece ser o mais usado atualmente.

Preocupações microarquiteturais de baixo nível

Depois de reler seus comentários, percebi que você também está interessado na sinalização que ocorre entre processador e memória. Os caches modernos geralmente permitem que várias falhas sejam simultaneamente destacadas. Isso é chamado de cache sem bloqueio (Kroft, (ISCA-8): 81-87, 1981). (Mas o documento é difícil de encontrar on-line e um tanto difícil de ler. Resposta curta: há muita contabilidade, mas você lida com isso. A estrutura de contabilidade de hardware é chamada MSHR (falta de informações / registro de status) ), que é o nome que Kroft deu em seu trabalho de 1981).


Obrigado resposta realmente abrangente, vou tentar e olhar para o cache sem bloqueio. Minha pergunta mal formulada estava realmente procurando confirmar que os processadores continuaram com cargas e armazenamentos durante um acesso à memória principal e que técnicas de microarquitetura foram usadas para fazer isso.
102948239408

+1, 1. É realmente processamento em barril se a programação round-robin não for usada? A Wikipedia o torna sinônimo de FGMT. (Posso aceitar aplicar o "processador barril" ao round robin com pular, embora isso interrompa a analogia como uma pauta ausente (cf. thread não pronto) não contrai a circunferência de um barril (acho que os processadores barris "verdadeiros" foram ? talvez raro-processador periférico para o CDC 6600 -Porque eles perdem um ciclo, mas ele faz hardware simplificar) 2. a menção de SoEMT como Hyper-threading da Itanium e Northstar da IBM et al parece especialmente apropriada dada a questão...
Paul A. Clayton

@ 102948239408, outra coisa que você pode pesquisar no Google são termos como "hit under miss" e "miss under miss" (a outra opção é "stall under miss", mas eu apenas tentei e parece não retornar nada útil.) termos usados ​​atualmente por (alguns) arquitetos para diferentes opções do que o cache pode permitir.
Wandering Logic

@ PaulA.Clayton, a terminologia definitivamente não é o meu forte. Concordo com você que o processamento de barril deve significar round-robin. Mas não consigo pensar em nenhum outro termo que signifique: intercalação ciclo a ciclo de um monte de threads em ordem (que é o que GPUs, Xeon Phi e Sun T1 fazem). É FGMT? Eu sempre pensei em FGMT como incluindo SMT, (ou seja, não especifica que os threads devem ser executados em ordem), mas talvez FGMT seja melhor que "processador de barril" para este caso?
Wandering Logic

O artigo do processador Barrel da Wikipedia declara: "também conhecido como" multithreading temporal "intercalado" ou "refinado", portanto IMT e FGMT são pelo menos termos reconhecidos. Eu acho que li "refinado" mais do que "intercalado", mas intercalado não é incomum. Eu geralmente usei FG (para mim "granular" implica mais separação do que o SMT fornece); O FG tem a vantagem de que o intercalado poderia se aplicar ao SoEMT. Eu suspeito que isso seja apenas uma mudança no uso do "processador de barril" que terei que sorrir (aguentar os dentes) e suportar.
Paul A. Clayton

16

A resposta curta é: nada, o processador trava.

Não há tantas possibilidades. Mudar para uma tarefa diferente não é realmente uma opção por dois motivos. Essa é uma operação cara e, como a tarefa atual e outras tarefas estão competindo por espaço no cache, a mudança para a outra tarefa pode exigir um acesso à memória principal e, assim, retornar à tarefa original. Além disso, isso teria que envolver o sistema operacional, portanto o processador teria que acionar alguma forma de interrupção ou interceptação - na verdade, o processador estaria mudando para algum código do kernel.

Enquanto o processador está parado, o timer continua a funcionar, portanto, pode haver uma interrupção no timer ou nos outros periféricos. Portanto, é mais provável que uma troca de contexto ocorra durante um acesso à memória principal do que durante um acesso ao cache, mas apenas porque leva mais tempo.

No entanto, os computadores modernos incluem uma variedade de técnicas para tentar reduzir o tempo perdido no processador aguardando a memória principal. Parar acontece, mas apenas quando não podia ser evitado.

Uma técnica é a busca especulativa : o processador tenta adivinhar qual local da memória será acessado e o busca em cache antes do tempo. Por exemplo, loops sobre um bloco de memória são comuns; portanto, se as linhas de cache foram carregadas para os endereços de memória 0x12340000, 0x12340010 e 0x12340020, pode ser uma boa ideia carregar a linha para 0x12340030. O compilador pode ajudar gerando instruções de pré-busca que são como cargas, exceto que eles transferem apenas dados da memória principal para o cache, e não para um registro do processador.

Outra técnica é a execução especulativa . O processador começa a executar a próxima instrução antes que a carga seja executada. Isso acontece naturalmente de qualquer maneira devido ao pipeline de instruções. Somente instruções que não dependem do valor carregado podem ser executadas desta maneira: o processador deve executar uma análise de dependência. Para instruções condicionais (por exemplo, carregar r1; ramificar se r1 ≠ 0), os processadores empregam heurísticas de previsão de ramificação para adivinhar qual será o valor. A execução especulativa após uma carga pode precisar ser rebobinada, caso a carga provoque um cancelamento.

Algumas arquiteturas como o Itanium facilitam a execução de instruções em uma ordem conveniente, permitindo o reordenamento de instruções por padrão: em vez de consistir em uma sequência de instruções elementares que são semanticamente executadas uma após a outra, os programas consistem em palavras muito longas : uma única instrução inclui muitas operações que devem ser executadas em paralelo por diferentes componentes do processador.

A mudança para outro encadeamento acontece no hyperthreading , encontrado nos processadores x86 de ponta. Essa é uma técnica de design de hardware: cada núcleo do processador contém dois bancos de registros separados (cada um correspondente a um contexto de tarefa), mas uma instância única de outros elementos, para que ele possa suportar dois encadeamentos de execução independentes, mas executar efetivamente apenas instruções de um em um tempo. Enquanto um segmento está parado, o outro segmento continua. Do ponto de vista do software, existem dois processadores independentes; acontece que esses processadores compartilham muitos componentes sob o capô.

A troca é mais um nível na hierarquia do cache de memória: a memória principal pode ser vista como um cache para o espaço de troca. Com a troca, os mecanismos e as taxas de desempenho são diferentes. Se uma tarefa precisar que os dados sejam carregados do swap, a instrução load acionará uma interceptação que executa o código do kernel para alocar uma página na RAM e carregar seu conteúdo do disco. Enquanto isso acontece, o kernel pode decidir mudar para outra tarefa.


Contrastando o primeiro e o penúltimo parágrafo, o "truque" é que nenhuma mudança de contexto real precisa acontecer com o hyperthreading, certo? A CPU mantém totalmente dois contextos ao mesmo tempo.
Raphael

11
@Raphael Right: no que diz respeito ao software, para tudo, menos para o desempenho, existem duas CPUs.
Gilles 'SO- stop be evil'

Uma CPU com hyperthread tem muitas unidades de execução semi-independentes (somadores de ponto flutuante e inteiro, multiplicadores, etc.), e acho que os dois contextos podem usar unidades de execução separadas simultaneamente - embora não tenham 100% de certeza disso.
Russell Borogove 30/08/14

@RussellBorogove Sim, eu não mencioná-lo porque mesmo CPUs não-hyperthreaded pode ter vários ALU / FPU / ... e vice-versa núcleos separados, por vezes, compartilhar FPU etc.
Gilles 'SO parada sendo mal'

5

A resposta a esta pergunta varia de acordo com a arquitetura em questão. Embora muitas CPUs parem (ARM, x86 sem hyperthreading etc.) porque leva muito tempo para alternar threads, essa não é a abordagem adotada por todas as arquiteturas. Em algumas arquiteturas, cada encadeamento agendado em uma CPU possui seu próprio arquivo de registro independente; portanto, o processador pode simplesmente executar o trabalho a partir de um encadeamento que não está aguardando um acesso à memória. Entendo que isso é, até certo ponto, o que o hyperthreading x86 faz (usando apenas 2 threads), mas é muito mais comum no GPGPUarquiteturas. No caso específico da CUDA, pelo menos dezenas, se não centenas, de warps de threads geralmente são carregados em um determinado multiprocessador a qualquer momento, com cada thread (centenas ou milhares deles) tendo seus próprios registros. Isso permite que a arquitetura execute uma instrução de outro encadeamento no ciclo seguinte, quando um determinado encadeamento emite um acesso à memória. Portanto, contanto que muitos threads sejam carregados, os núcleos do processador nunca ficam inativos para acessar a memória. Consulte as Diretrizes de desempenho e a hierarquia de memória para obter mais informações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.