Como a previsão de ramificação funciona, se você ainda precisa verificar as condições?

30

Eu estava lendo a resposta popular sobre a Predição de Filial em https://stackoverflow.com/q/11227809/555690 , e há algo que me confunde:

  • Se você adivinhou certo, continua.
  • Se você adivinhou errado, o capitão irá parar, recuar e gritar com você para apertar o botão. Em seguida, ele pode reiniciar no outro caminho.

Se você acertar sempre, o trem nunca terá que parar.

Se você adivinhar errado com muita frequência, o trem passará muito tempo parando, fazendo backup e reiniciando.

Mas é isso que eu não entendo: para saber se seu palpite estava certo ou errado, você precisa fazer uma verificação da condição de qualquer maneira . Então, como a previsão de ramificação funciona, se você ainda está fazendo a mesma verificação condicional?

O que estou tentando dizer é que a previsão de ramificação não é exatamente o mesmo que não ter nenhuma previsão de ramificação porque você está fazendo as mesmas verificações condicionais? (obviamente estou errado, mas não entendi)

Ómega
fonte
11
Este artigo da wiki faz um bom trabalho explicando-o.
Enderland 02/03
8
Uma CPU moderna é canalizada e pode fazer várias coisas ao mesmo tempo. Assim, ele pode começar a executar seu palpite enquanto ainda está tentando descobrir se acertou. Se o palpite estiver correto, o pipeline continuará em execução. Por um palpite errado, o pipeline é descartado e a execução é reiniciada a partir do ponto de "resposta correta".
markspace
2
Leitura relacionada: pipeline . Eu também recomendaria reler a resposta aceita para essa pergunta SO, pois ela responde aqui.

Respostas:

19

Obviamente, a condição é verificada todas as vezes. Mas, no momento em que é verificado, já está no pipeline da CPU. Nesse meio tempo, outras instruções também entraram no pipeline e estão em vários estágios de execução.

Geralmente, uma condição é imediatamente seguida por uma instrução de ramificação condicional, que ramifica se a condição for avaliada como TRUE ou será reprovada se a condição for avaliada como FALSE. Isso significa que existem dois fluxos de instruções diferentes que podem ser carregados no pipeline após a instrução de condição e a instrução de ramificação, dependendo se a condição é avaliada como TRUE ou FALSE. Infelizmente, imediatamente após o carregamento da instrução de condição e da instrução de ramificação, a CPU ainda não sabe o que avaliará a condição, mas ainda precisará carregar o material no pipeline. Portanto, ele escolhe um dos dois conjuntos de instruções com base em um palpite sobre o que a condição avaliará.

Posteriormente, conforme a instrução de condição percorre o oleoduto, é hora de ser avaliada. Nesse momento, a CPU descobre se seu palpite estava certo ou errado.

Se o palpite estiver correto, o ramo foi para o local correto e as instruções corretas foram carregadas no pipeline. Se o palpite estiver errado, todas as instruções que foram carregadas no pipeline após a instrução de ramificação condicional estavam erradas, elas precisam ser descartadas e a busca de instruções deve começar novamente no lugar certo.

Alteração

Em resposta ao comentário do StarWeaver, para ter uma idéia do que a CPU deve fazer para executar uma única instrução:

Considere algo tão simples como o MOV AX,[SI+10]que nós humanos imaginamos como "carregamos o AX com a palavra SI mais 10". Aproximadamente, a CPU precisa:

  1. emita o conteúdo do PC (o "registro do contador de programa") para o barramento de endereços;
  2. leia o código de operação da instrução no barramento de dados;
  3. incremento de PC;
  4. decodifique o código de operação para descobrir o que fazer com ele;
  5. emitir o conteúdo do PC para o barramento de endereços;
  6. leia o operando da instrução (neste caso 10) no barramento de dados;
  7. incremento de PC;
  8. alimente o operando e o SI ao somador;
  9. emitir o resultado do adicionador para o barramento de endereços;
  10. leia o AX no barramento de dados.

São 10 etapas impressionantes. Algumas dessas etapas serão otimizadas, mesmo em CPUs sem pipeline, por exemplo, a CPU quase sempre incrementará o PC em paralelo com a próxima etapa, o que é fácil, pois o PC é um registro muito, muito especial. nunca usado para qualquer outro trabalho, portanto, não há possibilidade de contenção entre diferentes partes da CPU para acessar esse registro específico. Porém, ainda temos 8 etapas para uma instrução tão simples e observe que já estou assumindo algum grau de sofisticação em nome da CPU, por exemplo, estou assumindo que não haverá necessidade de uma etapa extra inteira para o adicionador para efetivamente realizar a adição antes que o resultado possa ser lido dela,

Agora, considere que existem modos de endereçamento mais complicados, como MOV AX, [DX+SI*4+10]e instruções ainda mais complicadas, como os MUL AX, operandque realmente executam loops dentro da CPU para calcular seu resultado.

Portanto, meu argumento aqui é que a metáfora do "nível atômico" está longe de ser adequada ao nível de instrução da CPU. Pode ser adequado para o nível de etapa do pipeline, se você não quiser ir muito longe até o nível real da porta lógica.

Mike Nakis
fonte
2
Ah, eu me pergunto se parte do problema que as pessoas (inclusive eu) têm sobre entender isso é que é muito difícil (para mim, de qualquer maneira) imaginar uma CPU tendo apenas conhecimento parcial de uma única instrução; ou para ter um monte de instruções semi-acabadas "passando pelo forno de cinto de pizza" ... para mim, pelo menos, parece uma mudança de escala para o atômico quando estou acostumado a trabalhar com coisas entre o conjunto de montagem e o nível do torno de metal.
StarWeaver 4/03/15
11
@StarWeaver Gostei do seu comentário, por isso alterei minha resposta para resolvê-lo.
Mike Nakis
11
Uau, boa explicação. Costumo esquecer o quanto é necessário apenas mover palavras para locais mais úteis. Ainda estou visualizando uma CPU como um conjunto de fornos de pizza acionados por correia: 3.
StarWeaver 4/03/15
Vale lembrar que a questão Stack Overflow vinculada ao OP - aquela com 1,3 milhão de visualizações que provavelmente introduziu mais de 1 milhão de programadores ao fato anteriormente desconhecido de que a "predição de ramificação" ainda existe - exibe um exemplo em Java . Para pessoas como eu, que estão acostumadas a trabalhar no nível de abstração que linguagens como Java nos fornecem, até MOV AX,[SI+10]são estranhas, não "simples"; hoje a maioria dos programadores nunca escreveu montagem. Nós não "pensamos ingenuamente" como significando algo.
Mark Amery
@ MarkAmery bem, tudo bem, eu pensei que é bastante óbvio que, por "nós humanos", quero dizer "nós humanos que ousamos escrever montagem". O que está sendo argumentado é que mesmo os programadores de linguagem assembly não estão pensando no pipeline o tempo todo, ou sequer o fazem.
Mike Nakis
28

Pense nisso como uma viagem sem GPS. Você chega a um cruzamento e acha que precisa virar, mas não tem certeza. Então você faz a curva, mas peça ao seu passageiro para verificar o mapa. Talvez você esteja a cinco quilômetros da estrada quando terminar de discutir sobre onde está. Se você estivesse certo, você está a cinco quilômetros a mais do que estaria se tivesse parado e discutido antes de virar. Se você estava errado, você tem que se virar.

Os pipelines da CPU funcionam da mesma maneira. No momento em que eles podem verificar a condição, eles já estão a caminho. A diferença é que eles não precisam dirigir os cinco quilômetros para trás, apenas perdem a vantagem. Isso significa que não há mal em tentar.

Karl Bielefeldt
fonte
2
Essa explicação é legal.
sharptooth
2

Pelo meu entendimento, a previsão de ramificação é mais útil quando a condição que você precisa verificar exige o resultado de algo caro ou ainda em andamento, e você estaria mexendo os polegares esperando o valor para avaliar a condição.

Com coisas como execução fora de ordem, você pode usar a previsão de ramificação para começar a preencher pontos vazios no pipeline que a CPU não seria capaz de usar. Em uma situação em que não há, por algum motivo, ciclos inativos no pipeline, então sim, não há ganho na previsão de ramificação.

Mas a chave aqui é que a CPU está iniciando o trabalho para uma das ramificações previstas porque ainda não pode avaliar a condição.

Cães
fonte
1

Forma curta:

Algumas CPUs podem começar a trabalhar em uma nova instrução antes de terminar a antiga. Estas são as CPUs que usam previsão de ramificação.

Um exemplo de pseudocódigo:

int globalVariable;
int Read(int* readThis, int* readThat)
{
    if ((globalVariable*globalVariable % 17) < 5)
       return *readThis;
    else
       return *readThat;
}

O código acima verifica uma condição e, com base no resultado, ele precisa retornar o valor armazenado no local da memória addThisou o valor armazenado em readThat. Se a previsão de ramificação predizer a condição true, a CPU já lerá o valor armazenado no local da memória addThisenquanto faz o cálculo necessário para avaliar a ifinstrução. Este é um exemplo simplificado.

Pedro
fonte
1

Sim, a condição é verificada de qualquer maneira. Mas a vantagem da previsão de ramificação é que você pode trabalhar em vez de aguardar o resultado da verificação da condição.

Digamos que você tenha que escrever uma redação e que possa ser sobre o tópico A ou o tópico B. Você sabe de ensaios anteriores que seu professor gosta mais do tópico A do que B e o escolhe com mais frequência. Em vez de esperar pela decisão dele, você pode começar a escrever o ensaio sobre o primeiro tópico. Agora, existem dois resultados possíveis:

  1. Você começou seu ensaio sobre o tópico errado e teve que largar o que escreveu até agora. Você precisa começar a escrever sobre o outro tópico e é o mesmo esforço de tempo como se você tivesse esperado.
  2. Você adivinhou certo e já trabalhou.

As CPUs modernas estão ociosas na maior parte do tempo porque aguardam respostas de E / S ou o resultado de outros cálculos. Este tempo pode ser usado para realizar alguns trabalhos futuros.

Mesmo que você precise descartar o que está fazendo neste período ocioso - é mais provável que seja mais eficaz se você tiver a capacidade de adivinhar qual caminho o programa escolherá. E as CPUs modernas têm essa capacidade.

Otomo
fonte