Boa pergunta, ou pelo menos uma com uma resposta interessante. Parte dessa resposta mostra um mundo em que as CPUs podem ter uma escala eficiente de largura em vez de vários núcleos separados. Modelos de licenciamento / preço seriam diferentes!
O resto explica por que eles não podem. Resumo:
- O custo de múltiplos núcleos é dimensionado quase linearmente
- O custo da ampliação do pipeline superescalar de um núcleo é escalonado ~ quadraticamente Isso é possível com força bruta suficiente, até certo ponto. O desempenho de encadeamento único é muito importante para o uso interativo (a latência de ponta a ponta importa, não apenas a taxa de transferência); portanto, as atuais CPUs high-end de núcleo grande pagam esse preço. por exemplo, Skylake (4 de largura), Ryzen (5 ou 6 de largura) e A12 da Apple (7 de largura para os grandes núcleos, 3 de largura para os pequenos núcleos com eficiência energética)
- O IPC em diminuição grave retorna apenas da ampliação do pipeline para além de 3 ou 4 de largura, mesmo com execução fora de ordem para encontrar o ILP . As falhas de ramificação e de cache são difíceis e ainda paralisam todo o pipeline.
Você não mencionou frequência, apenas IPC, mas a frequência de escala também é difícil. Uma frequência mais alta requer uma voltagem mais alta; portanto, a potência é escalonada com a frequência em cubo : ^1
diretamente da frequência e ^2
da tensão. (A energia armazenada no capacitor é dimensionada com V ^ 2, e a maior parte da energia dinâmica além da corrente de fuga é do bombeamento de carga para as cargas capacitivas dos portões + fios FET.)
Desempenho = frequência vezes IPC. (Dentro da mesma arquitetura. O SIMD mais amplo permite que você faça o mesmo trabalho com menos instruções e alguns ISAs são mais densos que outros, por exemplo, o MIPS geralmente requer mais instruções para fazer o mesmo trabalho que o x86 ou o AArch64.)
Os custos estão na área da matriz (custo de fabricação) e / ou energia (que indiretamente limita a frequência porque o resfriamento é difícil). Além disso, menor potência e desempenho por Watt é um objetivo em si, especialmente para dispositivos móveis (bateria) e servidores (densidade de energia / custos de refrigeração / custos de eletricidade).
Antes que o multi-core por soquete fosse uma coisa, você tinha sistemas com vários soquetes para casos de uso avançados, nos quais desejava mais taxa de transferência do que era possível com uma única CPU que poderia ser fabricada, portanto esses eram os únicos sistemas SMP. (Servidores, estações de trabalho de última geração).
Se um único núcleo pudesse ser dimensionado com a eficiência que você desejasse, teríamos sistemas com 1 núcleo físico por soquete e SMT (por exemplo, HyperThreading) para permitir que eles atuassem como múltiplos núcleos lógicos. Os desktops / laptops típicos teriam apenas um núcleo físico e não teríamos dificuldade em paralelizar coisas que não são dimensionadas linearmente com mais núcleos. por exemplo, make -j4
para aproveitar os servidores com vários soquetes e / ou ocultar a latência de E / S em uma área de trabalho. (Ou talvez ainda tentássemos paralelizar muito se a largura do pipeline fosse dimensionada facilmente, mas o IPC não o fizesse, então tivemos que usar mais encadeamentos SMT.) apresentar SMT para o sistema operacional era muito diferente; portanto, algoritmos de bloqueio paralelo e bloqueio ainda seriam necessários lá.
Donald Knuth disse em uma entrevista de 2008
Eu também poderia expor um pouco a minha infelicidade pessoal com a tendência atual em direção à arquitetura multicore. Para mim, parece mais ou menos que os projetistas de hardware ficaram sem ideias e estão tentando passar a culpa do futuro desaparecimento da Lei de Moore para os criadores de software , dando-nos máquinas que trabalham mais rápido apenas em algumas poucas. principais parâmetros de referência!
Sim, se pudéssemos ter CPUs milagrosas de núcleo único com 8x de taxa de transferência em programas reais , provavelmente ainda as estaríamos usando. Com sistemas de soquete duplo, apenas quando vale a pena pagar muito mais por mais rendimento (não desempenho de thread único).
Múltiplas CPUs reduzem os custos de troca de contexto quando vários programas estão em execução (permitindo que eles funcionem paralelamente, em vez de alternar rapidamente entre eles); multitarefa preventiva interrompendo a maquinaria maciça e fora de ordem que uma CPU exigiria provavelmente machucaria ainda mais do que agora.
Fisicamente, ele seria de núcleo único (para uma hierarquia de cache simples, sem interconexões entre núcleos), mas suportaria SMT (por exemplo, HyperThreading da Intel), para que o software pudesse usá-lo como 8 núcleos lógicos que competem dinamicamente pelos recursos de taxa de transferência. Ou quando apenas 1 thread está em execução / não está parado, ele obtém o benefício completo.
Então, você usaria vários encadeamentos quando isso fosse realmente mais fácil / natural (por exemplo, processos separados sendo executados ao mesmo tempo) ou para problemas facilmente paralelizados com cadeias de dependência que impediriam maximizar o IPC dessa fera.
Infelizmente, porém, é uma ilusão da parte de Knuth que as CPUs com vários núcleos deixem de ser uma coisa neste momento.
Escala de desempenho de thread único
Eu acho que se eles fizessem um equivalente de 1 núcleo de uma CPU de 8 núcleos, esse núcleo teria um aumento de 800% no IPC, para que você obtivesse o desempenho completo em todos os programas, não apenas naqueles otimizados para vários núcleos.
Sim, é verdade. Se fosse possível construir tal CPU , seria muito surpreendente. Mas acho que é literalmente impossível no mesmo processo de fabricação de semicondutores (ou seja, a mesma qualidade / eficiência dos transistores). Certamente não é possível com o mesmo orçamento de energia e área de matriz que uma CPU de 8 núcleos, mesmo que você economize na lógica para colar núcleos e não precise de muito espaço para caches privados por núcleo.
Mesmo que você permita aumentos de frequência (como o critério real é trabalhar por segundo, não funcionar por relógio), tornar a CPU ainda 2x mais rápida seria um grande desafio.
Se fosse possível, em qualquer lugar próximo do mesmo orçamento de energia e área de matriz (assim, o custo de fabricação), construir essa CPU, sim, os fornecedores de CPU já as construiriam dessa maneira.
Especificamente, mais núcleos ou núcleos mais amplos? seção, para obter os antecedentes necessários para entender esta resposta; ele começa simples com o funcionamento de CPUs em pipeline em ordem e, em seguida, superescalar (várias instruções por relógio). Em seguida, explica como atingimos o muro de força por volta da era P4, levando ao fim da escala de frequência fácil, deixando principalmente apenas o IPC e realizando mais trabalho por instrução (por exemplo, SIMD) como o caminho a seguir, mesmo com transistores menores.
A ampliação de um pipeline (máximo de instruções por relógio) geralmente aumenta em custo como largura ao quadrado . Esse custo é medido na área da matriz e / ou energia, para uma verificação de dependência paralela mais ampla (detecção de perigos) e um agendador fora de serviço mais amplo para encontrar instruções prontas para execução. E mais portas de leitura / gravação no arquivo de registro e no cache, se você quiser executar instruções diferentes de nop
. Especialmente se você tiver instruções de 3 entradas, como FMA ou add-with-carry (2 registros + sinalizadores).
Também há retornos decrescentes do IPC para ampliar as CPUs ; a maioria das cargas de trabalho possui ILP (Paralelismo no Nível da Instrução) de pequena escala / curto alcance para as CPUs explorarem, portanto, aumentar o núcleo não aumenta o IPC (instruções por relógio) se o IPC já estiver limitado a menos do que a largura do núcleo por cadeias de dependência, falhas de ramificação, falhas de cache ou outras paradas. Claro que você obteria uma aceleração em alguns loops desenrolados com iterações independentes, mas não é isso que a maioria dos códigos passa a maior parte do tempo fazendo. As instruções de comparação / ramificação representam 20% da combinação de instruções no código "típico", IIRC. (Acho que li números de 15 a 25% para vários conjuntos de dados.)
Além disso, uma falta de cache que interrompe todas as instruções dependentes (e tudo quando a capacidade do ROB é atingida) custa mais para uma CPU mais ampla. (O custo de oportunidade de deixar mais unidades de execução ociosas; mais trabalho potencial não está sendo realizado.) Ou um erro de ramificação da mesma forma causa uma bolha.
Para obter 8x o IPC, precisaríamos de pelo menos uma melhoria de 8x na precisão da previsão de ramificação e nas taxas de acerto do cache . Mas as taxas de acerto do cache não se adaptam bem à capacidade do cache além de um certo ponto para a maioria das cargas de trabalho. E a pré-busca de HW é inteligente, mas não pode ser tão inteligente. E com 8x do IPC, os preditores de ramificação precisam produzir 8 vezes mais previsões por ciclo, além de serem mais precisos.
As técnicas atuais para a criação de CPUs de execução fora de ordem só podem encontrar ILP em intervalos curtos . Por exemplo, o tamanho do ROB do Skylake é 224 uops de domínio fundido, o planejador para uops não executados é 97 de domínio não fundido. Consulte Compreendendo o impacto do lfence em um loop com duas longas cadeias de dependência, para aumentar os comprimentos de um caso em que o tamanho do planejador é o fator limitante na extração do ILP de duas longas cadeias de instruções, se elas forem muito longas. E / ou veja esta resposta mais geral e introdutória ).
Portanto, encontrar o ILP entre dois loops longos separados não é algo que podemos fazer com o hardware. A recompilação binária dinâmica para fusão de loop pode ser possível em alguns casos, mas as CPUs difíceis e não algo que realmente podem fazer, a menos que sigam a rota Transmeta Crusoe. (camada de emulação x86 em cima de um ISA interno diferente; nesse caso, VLIW). Porém, os projetos x86 modernos padrão com caches uop e decodificadores poderosos não são fáceis de superar na maioria dos códigos.
E fora do x86, todos os ISAs ainda em uso são relativamente fáceis de decodificar, portanto, não há motivação para a recompilação dinâmica além das otimizações de longa distância. TL: DR: esperar que os compiladores mágicos que podem expor mais ILP ao hardware não funcionou para o Itanium IA-64 e é improvável que funcione para uma CPU super ampla para qualquer ISA existente com um modelo serial de execução.
Se você tinha uma CPU super ampla, definitivamente desejaria que ela suportasse o SMT, para que você possa mantê-lo alimentado com o trabalho a executar executando vários threads de baixo ILP.
Como o Skylake atualmente tem 4 uops de largura (e alcança um IPC real de 2 a 3 uops por relógio, ou ainda mais perto de 4 no código de alto rendimento), uma hipotética CPU 8x mais ampla seria de 32!
Ser capaz de gravar isso de volta em 8 ou 16 CPUs lógicas que compartilham dinamicamente esses recursos de execução seria fantástico: os threads não paralisados obtêm toda a largura de banda do front-end e a taxa de transferência de back-end.
Porém, com 8 núcleos separados, quando um encadeamento é interrompido, não há mais nada para manter as unidades de execução alimentadas; os outros threads não se beneficiam.
A execução geralmente é rápida: ela fica parada aguardando um carregamento incorreto do cache e, assim que chega muitas instruções em paralelo, pode usar esse resultado. Com uma CPU super ampla, essa explosão pode ir mais rápido e pode realmente ajudar com o SMT.
Mas não podemos ter CPUs mágicas super amplas
Portanto, para obter rendimento, precisamos expor o paralelismo ao hardware na forma de paralelismo no nível do encadeamento . Geralmente, os compiladores não são bons em saber quando / como usar threads, exceto em casos simples, como loops muito grandes. (OpenMP ou gcc -ftree-parallelize-loops
). Ainda é preciso ter inteligência humana para refazer o código para obter um trabalho útil eficiente em paralelo, porque a comunicação entre threads é cara e a inicialização do thread também.
O TLP é um paralelismo de granulação grossa, diferentemente do ILP de granulação fina em um único encadeamento de execução que o HW pode explorar.
As CPUs voltadas para cargas de trabalho interativas (como Intel / AMD x86 e núcleos de ponta Apple / ARM AArch64) definitivamente contribuem para os retornos decrescentes do escalonamento IPC, porque o desempenho de thread único ainda é tão valioso quando a latência importa, não apenas a taxa de transferência. problemas massivamente paralelos.
Ser capaz de executar 8 cópias de um jogo em paralelo a 15fps cada é muito menos valioso do que ser capaz de executar uma cópia a 45fps. Os fornecedores de CPU sabem disso, e é por isso que as CPUs modernas usam execução fora de ordem, mesmo que custe energia e área de matriz significativas. (Mas as GPUs não o fazem porque sua carga de trabalho já é massivamente paralela).
O hardware Xeon Phi de muitos núcleos da Intel (Knight's Landing / Knight's Mill) é um ponto interessante: execução fora de ordem muito limitada e SMT para manter núcleos de 2 largos alimentados com instruções SIMX AVX512 para processar números. Os núcleos são baseados na arquitetura Silvermont de baixa potência da Intel. (Executor avariado, mas com uma pequena janela de reordenação, muito menor que a família Sandybridge de grande porte. E um pipeline mais estreito.)
BTW, tudo isso é ortogonal ao SIMD. Obter mais trabalho por instrução sempre ajuda, se for possível para o seu problema.
Modelos de preços
Os modelos de preços de software são baseados no cenário atual de hardware.
Os modelos de licenciamento por núcleo tornaram-se mais difundidos (e relevantes até para desktops de soquete único) com o advento de CPUs com vários núcleos. Antes disso, era relevante apenas para servidores e grandes estações de trabalho.
Se o software não precisasse de múltiplos núcleos para rodar na velocidade máxima, não haveria realmente uma maneira de vendê-lo mais barato para pessoas que não estão obtendo tanto benefício porque o executam em uma CPU mais fraca. A menos que talvez o ecossistema de software / hardware tenha desenvolvido controles em "canais SMT" que permitem configurar uma largura máxima de execução para o código em execução nesse núcleo lógico. (Imaginando novamente um mundo em que as CPUs escalam na largura do pipeline em vez de vários núcleos separados.)