Por que os mecanismos precisam ser otimizados para novos processadores da mesma arquitetura?


39

Quando uma nova geração de processador é lançada, a maioria dos sites informa que os mecanismos e programas de jogos precisam ser otimizados para o novo hardware. Eu não entendo bem o porquê. Um processador geralmente possui uma arquitetura que define que tipo de conjunto de instruções ele usa. O que todos nós usamos hoje em dia é amd_x86_64. Por que qualquer programa ou compilador precisaria ser atualizado se todos os processadores usassem essa mesma arquitetura? Certamente, existem recursos dentro do pipeline do novo processador que otimizam a execução do código da máquina, mas por que o próprio código da máquina precisaria ser alterado se a arquitetura não o fizesse?


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Josh

14
"Necessidade" é uma expressão errada, e mais marketing do que verdade, da mesma maneira que, por exemplo, o Windows precisa suportar uma certa nova geração de CPU (ou não, como no caso do Windows 7, que, em princípio, funcionaria perfeitamente) por exemplo, Ryzen, exceto pelo uso de 3-4% a mais de energia do que o necessário). Esse ajuste é apenas para tentar extrair um pouco mais da CPU, aproximando-se do máximo. Realisticamente, você poderá obter de 1 a 2% em exemplos não planejados devido a agendamentos diferentes e usando algumas instruções novas.
Damon

2
Só porque dois processadores pode executar as mesmas operações que não significa que as operações têm o mesmo desempenho em ambos os processadores ...
Mehrdad

Veja uma pergunta minha sobre Stack Overflow: Como o mtune realmente funciona?
Marc.2377

Respostas:


54

Porque diferentes gerações da mesma arquitetura pode ter diferentes conjuntos de instruções .

Por exemplo, as extensões Streaming SIMD são provavelmente o conjunto de instruções x86 mais conhecido, mas, ainda assim, e apesar de haver apenas uma arquitetura x86, existem SSE, SSE2, SSE3 e SSE4.

Cada uma dessas gerações pode incluir novas instruções que fornecem maneiras mais rápidas de executar determinadas operações. Um exemplo que seria relevante para os jogos pode ser as instruções do produto de ponto.

Portanto, se um mecanismo de jogo for compilado para uma geração anterior de uma arquitetura, ele não terá suporte para essas instruções mais recentes. Da mesma forma, pode ser necessário otimizar o mecanismo para obter instruções mais recentes; O SSE4 , por exemplo, tem suporte para instruções de produtos de ponto que funcionam com dados de matriz de estruturas. Uma otimização que poderia tirar proveito dessas instruções mais recentes seria alterar o layout dos dados para matriz de estruturas.


11
@ Panzercrisis - obrigado pela sugestão de edição. Para ficar claro: a pergunta original não era sobre seu próprio código, era sobre o código do mecanismo, portanto "otimizar seu próprio código" não é uma boa sugestão de edição. No entanto, destacou que eu precisava esclarecer que, quando disse "otimizar", quis dizer "otimizar o código do mecanismo", por isso editei para retomar isso.
Maximus Minimus

37

A resposta de Maximus está correta, só quero dar outra parte da história:

O próprio hardware muda de uma maneira que você precisa para alterar a forma como codifica, independentemente das instruções recém-introduzidas.

  • Aumento ou diminuição da quantidade de cache significa que você precisa se preocupar menos ou mais com problemas de otimização / invalidação de cache. Mais cache significa que, com dados pequenos, você pode se concentrar menos em garantir que os dados sejam contíguos sem se preocupar com o desempenho. Menos cache significa que isso pode ser um problema, e muito pouco cache significa que, com algumas estruturas de dados grandes, isso não importa.

  • Novos níveis de cache significam que você precisa pensar mais sobre como organizar conjuntos de dados ainda maiores (L1, vs L2, vs L3 vs L4).

  • Mais núcleos significa que você precisa pensar em como irá melhorar a aplicação de multiencadeamentos e como sua aplicação é escalada em um ambiente de multiprocessos.

  • Relógios mais rápidos significam que você precisa começar a pensar mais na latência da memória do que na velocidade de computação da CPU como um gargalo do seu sistema.

  • O número de FPUs em um sistema pode não mais corresponder ao número de ALUs inteiras por núcleo (a AMD tinha / tem arquiteturas como essa).

  • O número de ciclos de relógio necessários para calcular uma operação que diminuíram ou aumentaram.

  • O número de registros disponíveis foi alterado.

Tudo isso tem um impacto muito real no desempenho dos programas que fizeram suposições sobre a arquitetura subjacente no hardware anterior com o mesmo ISA, positivo ou negativo.


"Níveis de cache aumentados ou diminuídos significa que você precisa se preocupar menos com a coerência do cache." - praticamente qualquer CPU é coerente com o cache. Você quer dizer compartilhamento falso? Mesmo que praticamente qualquer CPU $ line é quase sempre 64 B ...
Maciej Piechotka

11
Maciej estava apenas aceitando sua afirmação sobre a coerência do cache :) Você provavelmente quis dizer "otimização do cache" ou algo assim. Coerência de cache é a capacidade de um sistema manter uma visão consistente da memória de forma transparente para o software, mesmo na presença de N caches independentes . Isso é completamente ortogonal ao tamanho. TBH, a afirmação não é realmente relevante, mas sua resposta (especialmente os pontos 5 e 6) aborda a questão melhor do que a IMO aceita :) Talvez enfatizar a diferença entre arquitetura e arquitetura em U fará com que ela se destaque mais.
Margaret Bloom

4
"como multiplicação demorando mais que adição, onde hoje em dia as intel e o amd CPUS modernos levam a mesma quantidade de tempo" Isso não é verdade. Nas arquiteturas em pipeline, é necessário diferenciar entre latência (quando o resultado estiver pronto) e taxa de transferência (quantas você pode fazer por ciclo). A adição de int nos modernos processadores Intel tem uma taxa de transferência de 4 e uma latência de 1. A multiplicação possui uma taxa de transferência 1 e uma latência 3 (ou 4). Essas são as coisas que mudam com cada arquitetura e precisam de otimização. Por exemplo, pdepleva 1 ciclo na intel, mas 6 na Ryzen, portanto, pode não querer usá-la na Ryzen.
19417 Christoph

2
@ Clearer Eu sei que estamos falando de CPUs aqui, mas você nunca programou para GPUs, não é? O mesmo código produz resultados tão diferentes no desempenho que muitas vezes você é forçado a considerar os recursos de hardware do CUDA. Daí eu vim com isso, o tamanho do cache (memória compartilhada, cache L1 gerenciado) realmente precisa ser levado em consideração na maneira como você codifica algo no CUDA.
whn

2
@Christoph está correto. O benchmark que você vincula é para um loop sobre uma matriz c[i] = a[i] OP b[i](ou seja, 2 cargas e 1 armazenamento por operação), de modo que os tempos são dominados pela largura de banda da memória devido à intensidade computacional muito baixa. O tamanho da matriz não é mostrado, então IDK, se couber no L1D. ( gcc4.9 -Ofastmuito provavelmente auto-vetorizou esses loops, então você nem está medindo o custo de operações escalares normais como parte de um código inteiro complexo). A primeira linha dessa página é IMPORTANTE: O feedback útil revelou que algumas dessas medidas são seriamente falhas. Uma grande atualização está a caminho .
Peter Cordes

2

Mesmo além de grandes mudanças, como o suporte a novas instruções, os fabricantes de microprocessadores estão constantemente modificando seus projetos para melhorar o desempenho, e cada novo design pode ter um desempenho relativo diferente para cada instrução ou técnica. Talvez você tenha escrito algum código sem ramificação cuidadosamente otimizado para o Modelo X, mas o Modelo Y possui um preditor de ramificação aprimorado que reduz a penalidade de erro de previsão para a versão sem ramificação do código (que também libera um registro para ser usado em outro lugar) . Talvez o Modelo Y suporte maior paralelismo de uma determinada instrução de alta latência, de modo que agora um loop desenrolado dessa instrução obtenha uma melhor taxa de transferência, enquanto no Modelo X uma sequência mais curta foi melhor.

Qualquer problema pode ser resolvido de várias maneiras, e todo programa é uma coleção interligada de trocas e alocações de recursos, do ponto de otimização. Mesmo pequenas alterações na disponibilidade desses recursos ou no custo de um determinado trecho de código em termos desses recursos, podem ter um efeito em cascata que oferece uma vantagem substancial de desempenho para um trecho de código ou outro. Mesmo se um chip atualizado tem "mais de tudo", como muito mais de cada coisa pode balançar o equilíbrio.

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.