Por que o x86 é feio? Por que é considerado inferior quando comparado a outros? [fechadas]

105

Recentemente, li alguns arquivos SO e encontrei declarações contra a arquitetura x86.

e muitos mais comentários como

Tentei pesquisar, mas não encontrei nenhum motivo. Não acho o x86 ruim provavelmente porque essa é a única arquitetura com a qual estou familiarizado.

Alguém pode gentilmente me dar razões para considerar o x86 feio / ruim / inferior em comparação com os outros.

garras
fonte
1
Estou indo com S&A com base nas respostas até agora, mas observarei de passagem que CISC não é um problema para o conjunto de instruções m68k. x86 é o que é e você pode mantê-lo.
dmckee --- ex-moderador gatinho
o que é "S&A"? "CISC não é um problema para o conjunto de instruções m68k." -- Por que não?
garras
5
Os chips da série motorala 68000 têm uma arquitetura altamente CISC, mas têm um conjunto de instruções uniforme, bastante ortogonal e muito fácil. Por que a diferença do x86? Eu não sei. Mas observe que há uma grande diferença entre complexidade no chip e complexidade no conjunto de instruções (ou seja, na interface que um programador de montagem vê).
dmckee --- ex-moderador gatinho
4
1 para uma pergunta muito interessante.
Turing Complete
1
Estudo recente sobre eficiência energética de diferentes processadores encontrado aqui, com uma boa discussão sobre o que impulsionou os projetos CISC e RISC. extremetech.com/extreme/…

Respostas:

93

Algumas razões possíveis para isso:

  1. x86 é um ISA relativamente antigo (afinal, seus progenitores eram 8086s)
  2. O x86 evoluiu significativamente várias vezes, mas o hardware é necessário para manter a compatibilidade com os binários antigos. Por exemplo, o hardware x86 moderno ainda contém suporte para a execução nativa de código de 16 bits. Além disso, existem vários modelos de endereçamento de memória para permitir que códigos antigos interoperem no mesmo processador, como modo real, modo protegido, modo 8086 virtual e modo longo (amd64). Isso pode ser confuso para alguns.
  3. x86 é uma máquina CISC. Por muito tempo, isso significava que era mais lento do que máquinas RISC, como MIPS ou ARM, porque as instruções têm interdependência de dados e sinalizadores, tornando difícil implementar a maioria das formas de paralelismo de nível de instrução. Implementações modernas traduzem as instruções x86 em instruções semelhantes a RISC chamadas " micro-ops " nos bastidores para tornar esses tipos de otimizações práticas para implementar em hardware.
  4. Em alguns aspectos, o x86 não é inferior, é apenas diferente. Por exemplo, a entrada / saída é tratada como mapeamento de memória na grande maioria das arquiteturas, mas não no x86. (NB: as máquinas x86 modernas normalmente têm alguma forma de suporte DMA e se comunicam com outro hardware por meio de mapeamento de memória; mas o ISA ainda tem instruções de E / S como INe OUT)
  5. O ISA x86 tem poucos registros de arquitetura, o que pode forçar os programas a percorrer a memória com mais frequência do que seria necessário. As instruções extras necessárias para fazer isso usam recursos de execução que poderiam ser gastos em trabalhos úteis, embora encaminhamento de loja eficientemantém a latência baixa. Implementações modernas com renomeação de registro em um grande arquivo de registro físico podem manter muitas instruções em andamento, mas a falta de registros arquitetônicos ainda era uma fraqueza significativa para x86 de 32 bits. O aumento do x86-64 de 8 para 16 registros inteiros e vetoriais é um dos maiores fatores no código de 64 bits sendo mais rápido que o de 32 bits (junto com a ABI de chamada de registro mais eficiente), não o aumento da largura de cada registro. Um aumento adicional de 16 para 32 registradores inteiros ajudaria um pouco, mas não tanto. (AVX512 aumenta para 32 registradores de vetor, no entanto, porque o código de ponto flutuante tem latência mais alta e geralmente precisa de mais constantes.) ( Veja o comentário )
  6. O código assembly x86 é complicado porque x86 é uma arquitetura complicada com muitos recursos. Uma lista de instruções para uma máquina MIPS típica cabe em um pedaço de papel do tamanho de uma carta. A listagem equivalente para x86 preenche várias páginas, e as instruções apenas fazem mais, então você geralmente precisa de uma explicação maior do que uma listagem pode fornecer. Por exemplo, a MOVSBinstrução precisa de um bloco relativamente grande de código C para descrever o que faz:

    if (DF==0) 
      *(byte*)DI++ = *(byte*)SI++; 
    else 
      *(byte*)DI-- = *(byte*)SI--;
    

    Essa é uma única instrução fazendo um carregamento, um armazenamento e duas adições ou subtrações (controladas por uma entrada de sinalizador), cada uma das quais seriam instruções separadas em uma máquina RISC.

    Embora a simplicidade do MIPS (e arquiteturas semelhantes) não os torne necessariamente superiores, para ensinar uma introdução à classe assembler faz sentido começar com um ISA mais simples . Algumas classes de montagem ensinam um subconjunto ultra-simplificado de x86 chamado y86 , que é simplificado além do ponto de não ser útil para uso real (por exemplo, sem instruções de deslocamento), ou algumas ensinam apenas as instruções básicas de x86.

  7. O x86 usa opcodes de comprimento variável, que adicionam complexidade de hardware com relação à análise de instruções. Na era moderna, esse custo está se tornando cada vez menor à medida que as CPUs se tornam cada vez mais limitadas pela largura de banda da memória do que pela computação bruta, mas muitos artigos e atitudes de "destruição de x86" vêm de uma época em que esse custo era comparativamente muito maior.
    Atualização de 2016: Anandtech postou uma discussão sobre tamanhos de opcode em x64 e AArch64 .

EDIT: Isso não deveria ser um bash no x86! festa. Eu tive pouca escolha a não ser criticar, dada a forma como a pergunta foi formulada. Mas, com exceção de (1), todas essas coisas foram feitas por bons motivos (ver comentários). Os designers da Intel não são estúpidos - eles queriam conseguir algumas coisas com sua arquitetura, e esses são alguns dos impostos que eles tiveram que pagar para tornar essas coisas uma realidade.

Billy ONeal
fonte
17
É uma troca. É um ponto forte porque o tamanho do binário pode ser menor, mas é um ponto fraco porque você precisa de um hardware muito complicado para implementar um analisador para essas instruções. A grande maioria das instruções têm o mesmo tamanho de qualquer maneira - a maior parte da razão para opcodes de comprimento variável no x86 é quando eles decidiram adicionar recursos e descobriram que não podiam representar o que queriam no número de bits com os quais tinham que trabalhar . A grande maioria das pessoas não está preocupada com o tamanho binário tanto quanto a complexidade do hardware ou consumo de energia.
Billy ONeal
8
@Joey Adams: Compare as instruções de comprimento variável do x86 com o Modo Thumb do ARM ( en.wikipedia.org/wiki/ARM_architecture#Thumb ). O Modo Thumb resulta em código de objeto significativamente menor para o ARM porque as instruções mais curtas são mapeadas diretamente para as instruções normais. Mas, como há um mapeamento 1: 1 entre as instruções maiores e as menores, o hardware de análise é simples de implementar. As instruções de comprimento variável do x86 não têm esses benefícios porque não foram projetadas dessa maneira.
Billy ONeal
7
(6) Nem todo op-code precisa ser usado por todos os programas, mas caramba, quando eu preciso de SSE3, fico feliz por tê-lo.
Chris K
4
@Chris Kaminski: Como isso não afeta o hardware? Claro, em um computador moderno de tamanho normal ninguém vai se importar, mas se estou fazendo algo como um telefone celular, me preocupo mais com o consumo de energia do que com quase qualquer outra coisa. Os opcodes de comprimento variável não aumentam o tempo de execução, mas o hardware de decodificação ainda requer energia para operar.
Billy ONeal
5
O que é uma das coisas que tornam o conjunto de instruções x86 tão feio, já que ele não pode decidir se é um acumulador ou uma arquitetura baseada em arquivo de registro (embora isso tenha sido corrigido principalmente com o 386, o que tornou o conjunto de instruções muito mais ortogonal , independentemente do que os fãs de 68k digam).
ninjalj
25

O principal golpe contra o x86 em minha mente são suas origens CISC - o conjunto de instruções contém muitas interdependências implícitas. Essas interdependências tornam difícil fazer coisas como reordenar instruções no chip, porque os artefatos e a semântica dessas interdependências devem ser preservados para cada instrução.

Por exemplo, a maioria das instruções de adição e subtração de inteiros x86 modificam o registro de sinalizadores. Depois de realizar uma adição ou subtração, a próxima operação geralmente é olhar o registrador de sinalizadores para verificar o estouro, bit de sinal, etc. Se houver outra adição depois disso, é muito difícil dizer se é seguro começar a execução da segunda adição antes que o resultado da primeira adição seja conhecido.

Em uma arquitetura RISC, a instrução add especificaria os operandos de entrada e o (s) registrador (es) de saída, e tudo sobre a operação ocorreria usando apenas esses registradores. Isso torna muito mais fácil desacoplar as operações de adição que estão próximas umas das outras porque não há nenhum registro de flags do bloomin 'forçando tudo a se alinhar e executar um único arquivo.

O chip DEC Alpha AXP, um projeto RISC estilo MIPS, era dolorosamente espartano nas instruções disponíveis, mas o conjunto de instruções foi projetado para evitar dependências implícitas de registro entre instruções. Não havia registro de pilha definido por hardware. Não havia registro de sinalizadores definidos por hardware. Até mesmo o ponteiro de instrução foi definido pelo sistema operacional - se você quisesse retornar para o chamador, tinha que descobrir como o chamador iria informá-lo para qual endereço retornar. Isso geralmente era definido pela convenção de chamada do sistema operacional. No x86, porém, é definido pelo hardware do chip.

De qualquer forma, ao longo de 3 ou 4 gerações de designs de chip Alpha AXP, o hardware deixou de ser uma implementação literal do conjunto de instruções espartano com 32 registros int e 32 registros flutuantes para um mecanismo de execução totalmente fora de ordem com 80 registros internos, renomeação de registro, encaminhamento de resultados (onde o resultado de uma instrução anterior é encaminhado para uma instrução posterior que depende do valor) e todos os tipos de impulsionadores de desempenho selvagens e loucos. E com todos esses sinos e assobios, o chip AXP ainda era consideravelmente menor do que o chip Pentium comparável da época, e o AXP era muito mais rápido.

Você não vê esses tipos de picos de desempenho aumentando as coisas na árvore genealógica do x86, principalmente porque a complexidade do conjunto de instruções do x86 torna muitos tipos de otimizações de execução proibitivamente caras, senão impossíveis. A genialidade da Intel foi desistir mais de implementar o conjunto de instruções x86 no hardware - todos os chips x86 modernos são, na verdade, núcleos RISC que, até certo ponto, interpretam as instruções x86, traduzindo-as em microcódigo interno que preserva toda a semântica do x86 original instrução, mas permite um pouco daquele RISC fora de ordem e outras otimizações sobre o microcódigo.

Eu escrevi muito sobre x86 assembler e posso apreciar totalmente a conveniência de suas raízes CISC. Mas eu não apreciei totalmente o quão complicado o x86 era até passar algum tempo escrevendo o assembler Alpha AXP. Fiquei pasmo com a simplicidade e uniformidade do AXP. As diferenças são enormes e profundas.

dthorpe
fonte
6
Não ouvirei nenhuma crítica ao CISC em si, a menos e até que você possa explicar m68k.
dmckee --- ex-moderador gatinho
2
Não estou familiarizado com o m68k, então não posso criticá-lo.
dthorpe
4
Não acho que essa resposta seja ruim o suficiente para votar negativamente, mas acho que todo o argumento "RISC é menor e mais rápido que o CISC" não é realmente relevante na era moderna. Claro, o AXP pode ter sido muito mais rápido para a época, mas o fato é que os RISCs modernos e os CISCs modernos são quase os mesmos quando se trata de desempenho. Como eu disse em minha resposta, a pequena penalidade de energia para decodificação de x86 é um motivo para não usar x86 para algo como um telefone móvel, mas isso é pouco argumento para um desktop ou notebook de tamanho completo.
Billy ONeal
4
@Billy: o tamanho é mais do que apenas o tamanho do código ou o tamanho da instrução. A Intel paga uma grande penalidade na área de superfície do chip para implementar a lógica de hardware para todas essas instruções especiais, com núcleo de microcódigo RISC sob o capô ou não. O tamanho da matriz impacta diretamente no custo de fabricação, portanto, ainda é uma preocupação válida com projetos de sistemas modernos.
dthorpe
1
@dthorpe: Discordo da maioria, senão de tudo, do que você escreveu. Desde o 8086, você não precisa se preocupar se é seguro executar um addapós o outro add. As regras são claras. Também não há necessidade de você lidar com a reordenação de instruções. Desde o Pentium Pro em meados dos anos 90, a CPU faz isso por você. O que você está mencionando pode ter sido um problema há 20 anos, mas não vejo nenhuma razão para considerá-lo contra a arquitetura x86 hoje em dia.
Nathan Fellman
21

A arquitetura x86 data do design do microprocessador 8008 e parentes. Essas CPUs foram projetadas em uma época em que a memória era lenta e se você pudesse fazer isso no chip da CPU, muitas vezes era um muito mais rápido. No entanto, o espaço da CPU também era caro. Essas duas razões são porque há apenas um pequeno número de registradores que tendem a ter propósitos especiais e um conjunto de instruções complicado com todos os tipos de pegadinhas e limitações.

Outros processadores da mesma época (por exemplo, a família 6502) também têm limitações e peculiaridades semelhantes. Curiosamente, tanto a série 8008 quanto a série 6502 foram concebidas como controladores incorporados. Mesmo naquela época, esperava-se que os controladores incorporados fossem programados em assembler e, de muitas maneiras, fornecidos ao programador de assembly, em vez de ao redator do compilador. (Veja no chip VAX o que acontece quando você atende à escrita do compilador.) Os designers não esperavam que eles se tornassem plataformas de computação de uso geral; era para isso que serviam os predecessores da arquitetura POWER. A revolução do computador doméstico mudou isso, é claro.

staticsan
fonte
4
+1 para a única resposta aqui de alguém que realmente parece ter antecedentes históricos sobre o assunto.
Billy ONeal
3
A memória sempre foi lenta. É possivelmente (relativamente falando) mais lento hoje do que quando comecei com o Z80s e o CP / M em 1982. A extinção não é o único caminho da evolução, porque com a extinção essa direção evolucionária particular pára. Eu diria que o x86 se adaptou bem em seus 28 anos (de existência até agora).
Olof Forshell
4
A velocidade da memória atingiu brevemente a paridade com as CPUs na época do 8086. O 9900 da Texas Instruments tem um design que só funciona porque isso aconteceu. Mas então a CPU disparou novamente e permaneceu lá. Só agora, existem caches para ajudar a gerenciar isso.
staticsan
3
@Olof Forshell: era compatível com o assembler, pois o código do assembly 8080 podia ser convertido em código 8086. Desse ponto de vista, eram 8080 mais extensões, da mesma forma que você poderia ver 8080 como 8008 mais extensões.
David Thornley
3
@Olof Forshell: Exceto que o 8086 foi projetado para que isso acontecesse. Era uma extensão do 8080, e a maioria (possivelmente todas) das instruções do 8080 mapeadas um a um, com semântica obviamente semelhante. Isso não é verdade para a arquitetura IBM 360, não importa de que maneira você deseja colocá-la.
David Thornley
13

Eu tenho alguns aspectos adicionais aqui:

Considere que a operação "a = b / c" x86 implementaria isso como

  mov eax,b
  xor edx,edx
  div dword ptr c
  mov a,eax

Como um bônus adicional da instrução div, edx conterá o restante.

Um processador RISC exigiria primeiro carregar os endereços de bec, carregar bec da memória para os registradores, fazer a divisão e carregar o endereço de a e então armazenar o resultado. Dst, sintaxe src:

  mov r5,addr b
  mov r5,[r5]
  mov r6,addr c
  mov r6,[r6]
  div r7,r5,r6
  mov r5,addr a
  mov [r5],r7

Aqui normalmente não haverá resto.

Se alguma variável deve ser carregada através de ponteiros, ambas as sequências podem se tornar mais longas, embora isso seja menos possível para o RISC, porque ele pode ter um ou mais ponteiros já carregados em outro registro. x86 tem menos registros, portanto, a probabilidade de o ponteiro estar em um deles é menor.

Prós e contras:

As instruções RISC podem ser misturadas com o código circundante para melhorar o agendamento da instrução, isso é menos possível com o x86 que, em vez disso, faz este trabalho (mais ou menos bem, dependendo da sequência) dentro da própria CPU. A sequência RISC acima normalmente terá 28 bytes de comprimento (7 instruções de 32 bits / 4 bytes de largura cada) em uma arquitetura de 32 bits. Isso fará com que a memória fora do chip funcione mais ao buscar as instruções (sete buscas). A sequência x86 mais densa contém menos instruções e, embora suas larguras variem, você provavelmente está vendo uma média de 4 bytes / instrução lá também. Mesmo se você tiver caches de instrução para acelerar isso, sete buscas significa que você terá um déficit de três em outro lugar para compensar em comparação com o x86.

A arquitetura x86 com menos registros para salvar / restaurar significa que provavelmente fará mudanças de thread e tratará interrupções mais rápido do que RISC. Mais registros para salvar e restaurar requerem mais espaço de pilha de RAM temporário para fazer interrupções e mais espaço de pilha permanente para armazenar estados de thread. Esses aspectos devem tornar o x86 um candidato melhor para executar RTOS puro.

Em uma nota mais pessoal, acho mais difícil escrever um assembly RISC do que x86. Eu resolvo isso escrevendo a rotina RISC em C, compilando e modificando o código gerado. Isso é mais eficiente do ponto de vista de produção de código e provavelmente menos eficiente do ponto de vista de execução. Todos esses 32 registros para acompanhar. Com x86 é o contrário: 6-8 registros com nomes "reais" tornam o problema mais gerenciável e instila mais confiança de que o código produzido funcionará conforme o esperado.

Feio? Isso está nos olhos de quem vê. Eu prefiro "diferente".

Olof Forshell
fonte
a, bec em meus exemplos devem ser vistos como variáveis ​​baseadas na memória e não como valores imediatos.
Olof Forshell
... "dword ptr" é usado para especificar o tamanho de uma variável cujo tamanho não é conhecido se, por exemplo, for simplesmente declarado como externo ou se você for preguiçoso.
Olof Forshell
2
Essa não é a primeira vez que ouvi a sugestão de escrever primeiro em C e depois destilar em assembler. Isso definitivamente ajuda
Joe Plante
No início, todos os processadores eram RISC. O CISC surgiu como uma estratégia de mitigação para sistemas de memória de núcleo férrico que eram MUITO lentos, portanto, o CISC, com menos instruções mais poderosas, colocava menos estresse no subsistema de memória e fazia melhor uso da largura de banda. Da mesma forma, os registros foram originalmente considerados como locais de memória no chip e na CPU para fazer acumulações. A última vez que fiz um benchmarking sério de uma máquina RISC foi em 1993 - SPARC e HP Prisim. SPARC foi horrível em todos os aspectos. O Prisim foi até 20x mais rápido do que um 486 em add / sub / mul, mas sugou em transcendentais. CISC é melhor.
@OlofForshell Você diz, there typically won't be a remindermas o wiki diz que os mips têm: en.wikipedia.org/wiki/MIPS_instruction_set#Integer
Alex Zhukovskiy
10

Acho que essa pergunta tem uma suposição falsa. São principalmente acadêmicos obcecados por RISC que chamam o x86 de feio. Na realidade, o ISA x86 pode fazer em uma única instrução operações que levariam de 5 a 6 instruções em ISAs RISC. Os fãs do RISC podem contestar que as CPUs x86 modernas quebram essas instruções "complexas" em microops; Contudo:

  1. Em muitos casos, isso é apenas parcialmente verdadeiro ou não é verdade. As instruções "complexas" mais úteis em x86 são coisas comomov %eax, 0x1c(%esp,%edi,4) , por exemplo, modos de endereçamento, e eles não são divididos.
  2. O que costuma ser mais importante nas máquinas modernas não é o número de ciclos gastos (porque a maioria das tarefas não é vinculada à CPU), mas o impacto do código no cache de instruções. 5-6 instruções de tamanho fixo (geralmente 32 bits) afetarão o cache muito mais do que uma instrução complexa que raramente tem mais de 5 bytes.

O x86 realmente absorveu todos os aspectos positivos do RISC cerca de 10-15 anos atrás, e as qualidades restantes do RISC (na verdade, a definição - o conjunto mínimo de instruções) são prejudiciais e indesejáveis.

Além do custo e da complexidade de fabricação de CPUs e seus requisitos de energia, x86 é o melhor ISA . Qualquer um que disser o contrário está deixando a ideologia ou a agenda atrapalharem seu raciocínio.

Por outro lado, se você está direcionando dispositivos embarcados onde o custo da CPU conta, ou dispositivos embarcados / móveis onde o consumo de energia é uma preocupação principal, ARM ou MIPS provavelmente fazem mais sentido. Lembre-se de que você ainda terá que lidar com a ram extra e o tamanho binário necessários para lidar com código que é facilmente 3-4 vezes maior e não será capaz de chegar perto do desempenho. Se isso importa, depende muito do que você estará executando nele.

R .. GitHub PARAR DE AJUDAR O GELO
fonte
3
onde o consumo de energia é uma preocupação principal, ARM ou MIPS provavelmente fazem mais sentido ... então, se há pelo menos um aspecto em que ARM ou MIPS fazem mais sentido, isso não torna o x86 não necessariamente o melhor ISA?
Shahbaz
É por isso que qualifiquei "o melhor" com "além do custo ... e suas necessidades de energia".
R .. GitHub PARAR DE AJUDAR O ICE
1
Eu acho que a Intel diminuindo a velocidade da CPU e tamanhos menores de dados eliminaram amplamente o diferencial de potência. A nova CPU dual de 64 bits Celeron com caches L1 de 64k e L2 de 1 MB é um chip de 7,5 watts. É a minha máquina de hangout "Starbucks", e a duração da bateria é ridiculamente longa e vai funcionar em círculos em torno de uma máquina P6. Como um cara que faz principalmente cálculos de ponto flutuante, desisti do RISC há muito tempo. Ele apenas rasteja. SPARC em particular era atrozmente glacial. O exemplo perfeito de por que o RISC é uma merda foi a CPU Intel i860. A Intel nunca foi LÁ novamente.
@RocketRoy: 7,5 watts não são realmente aceitáveis ​​para um dispositivo que funciona 24 horas por dia, 7 dias por semana (e não realiza cálculos úteis o tempo todo) ou que funciona com uma bateria de 3,7v / 2000mAh.
R .. GitHub PARAR DE AJUDAR O ICE
2
@RocketRoy "CPU Intel i860. Intel nunca foi LÁ novamente." Depois de um pouco de pesquisa, o i860 parece muito com Itanium: VLIW, paralelismo de instrução ordenado por compilador ....
Jonathon Reinhart
9

A linguagem assembler x86 não é tão ruim. É quando você chega ao código de máquina que ele começa a ficar realmente feio. Codificações de instruções, modos de endereçamento, etc. são muito mais complicados do que para a maioria das CPUs RISC. E há diversão extra incorporada para fins de compatibilidade com versões anteriores - coisas que só surgem quando o processador está em um determinado estado.

Nos modos de 16 bits, por exemplo, o endereçamento pode parecer totalmente bizarro; há um modo de endereçamento para [BX+SI], mas não um para[AX+BX] . Coisas como essa tendem a complicar o uso do registro, já que você precisa garantir que seu valor esteja em um registro que você possa usar conforme necessário.

(Felizmente, o modo de 32 bits é muito mais saudável (embora às vezes ainda seja um pouco estranho - segmentação, por exemplo), e o código x86 de 16 bits é amplamente irrelevante fora dos carregadores de inicialização e alguns ambientes incorporados.)

Também há as sobras dos velhos tempos, quando a Intel estava tentando fazer do x86 o processador definitivo. Instruções com alguns bytes de comprimento que executavam tarefas que ninguém mais faz, porque eram francamente lentas ou complicadas demais. As instruções ENTER e LOOP , para dois exemplos - observe que o código do frame da pilha C é como "push ebp; mov ebp, esp" e não "enter" para a maioria dos compiladores.

cHao
fonte
2
Acredito que o problema "enter" versus "push / mov" surgiu porque em alguns processadores, "push / mov" é mais rápido. Em alguns processadores, "enter" é mais rápido. É a vida.
Dietrich Epp
4
Quando fui forçado a usar uma máquina baseada em x86 e comecei a dar uma olhada nela (tendo experiência com m68k), comecei a me sentir frustrado com a programação, ... como se eu tivesse aprendido a programar com uma linguagem como C, e então ser forçado a entrar em contato com asm ... você "sente" que perde o poder de expressão, facilidade, clareza, "coerência", "intuicionabilidade". Tenho certeza que se eu tivesse iniciado a programação ASM com x86, teria pensado não é tão ruim ... talvez ... Eu também fiz MMIX e MIPS, e seu "asm lang" é muito melhor do que x86 (se este for o PoV correto para o Q, mas talvez não seja)
ShinTakezou
O problema do modo de endereçamento foi corrigido no 80386. Apenas o código de 16 bits tem modos de endereçamento limitados, o código de 32 bits é muito melhor. Você pode obter os modos de endereçamento de 32 bits em código de 16 bits usando um prefixo especial e vice-versa.
fuz
@FUZxxl: Sim ... eu provavelmente deveria ter mencionado que a feiura se limita principalmente ao código de 16 bits. Corrigido (eu acho). :)
cHao
A deselegância percebida vem principalmente do equívoco de que os registros de um 8086 são registros de uso geral; isso está incorreto. Cada um deles tem um propósito especial e se você não se ater aos seus propósitos, você vai passar maus momentos.
fuz
3

Não sou um especialista, mas parece que muitos dos recursos pelos quais as pessoas não gostam podem ser os motivos pelos quais ele tem um bom desempenho. Vários anos atrás, ter registradores (em vez de uma pilha), quadros de registradores, etc. eram vistos como boas soluções para fazer a arquitetura parecer mais simples para os humanos. Porém, hoje em dia, o que importa é o desempenho do cache, e as palavras de comprimento variável do x86 permitem que ele armazene mais instruções no cache. A "decodificação de instruções", que acredito que os oponentes apontaram uma vez que ocupou metade do chip, não é mais assim tanto.

Acho que o paralelismo é um dos fatores mais importantes hoje em dia - pelo menos para algoritmos que já rodam rápido o suficiente para serem utilizáveis. Expressar alto paralelismo no software permite que o hardware amortize (ou muitas vezes oculte completamente) as latências da memória. Claro, o futuro da arquitetura de maior alcance provavelmente está em algo como a computação quântica.

Ouvi da nVidia que um dos erros da Intel foi manter os formatos binários próximos ao hardware. O PTX do CUDA faz alguns cálculos rápidos de uso de registro (coloração de gráfico), então a nVidia pode usar uma máquina de registro em vez de uma máquina de pilha, mas ainda tem um caminho de atualização que não quebra todos os softwares antigos.

gatoatigrado
fonte
9
O RISC não foi projetado com desenvolvedores humanos em mente. Uma das ideias por trás do RISC era descarregar parte da complexidade do chip em quem escreveu o assembly, de preferência o compilador. Mais registros significa menos uso de memória e menos dependências entre as instruções, permitindo pipelines mais profundos e melhor desempenho. Observe que o x86-64 tem duas vezes mais registros gerais do que o x86, e só isso é responsável por ganhos de desempenho significativos. E as instruções na maioria dos chips x86 são decodificadas antes de serem armazenadas em cache, não depois (portanto, o tamanho não importa aqui).
Dietrich Epp
3
@Dietrich Epp: Isso não é totalmente verdade. O x86-64 tem mais registros visíveis no ISA, mas as implementações modernas do x86 geralmente têm um arquivo de registro de estilo RISC que é mapeado para os registros do ISA sob demanda para acelerar a execução.
Billy ONeal
"Ouvi da nVidia que um dos erros da Intel foi manter os formatos binários próximos ao hardware." - Não entendi isso e a parte PTX do CUDA.
garras
1
@Dietrech Epp: "E as instruções na maioria dos chips x86 são decodificadas antes de serem armazenados em cache, não depois de" Isso não é verdade. Eles são armazenados em cache antes de serem decodificados. Eu acredito que o Pentium 4 tinha um cache de rastreamento adicional que foi armazenado em cache após a decodificação, mas que foi descontinuado.
Nathan Fellman
isso não é verdade, os mais novos processadores de "ponte arenosa" usam uma espécie de cache de rastreamento (como aquele para o pentium 4, oh aquele velho: D), então as tecnologias vão embora e voltam ...
Quonux
3

Além dos motivos que as pessoas já mencionaram:

  • x86-16 tinha um esquema de endereçamento de memória bastante estranho que permitia que um único local de memória fosse endereçado de até 4096 maneiras diferentes, limitava a RAM a 1 MB e forçava os programadores a lidar com dois tamanhos diferentes de ponteiros. Felizmente, a mudança para 32 bits tornou esse recurso desnecessário, mas os chips x86 ainda carregam a cruft de registradores de segmento.
  • Embora não seja uma falha de x86 per se , convenções de chamada x86 não foram padronizados como MIPS foi (principalmente porque MS-DOS não veio com nenhum compiladores), deixando-nos com a confusão de __cdecl, __stdcall, __fastcall, etc.
dan04
fonte
Hmm ... quando penso em concorrentes x86, não penso em MIPS. ARM ou PowerPC talvez ....
Billy ONeal
@Billy: o x86 existe há quase uma eternidade. Ao mesmo tempo, o MIPS era um concorrente do x86. Pelo que me lembro, o x86 teve seu trabalho difícil para chegar a um nível em que fosse competitivo com o MIPS. (Na época em que MIPS e SPARC estavam lutando na arena da estação de trabalho.)
Shannon Severance
@Shannon Severance: Só porque algo já existiu, não significa que existe.
Billy ONeal
2
@supercat: o que as pessoas na era do modelo de memória plana x86-32 tendem a esquecer é que 16 bits significa 64k de memória (qualquer um que se preocupe em fazer as contas entenderá que mágica não é possível, que o 8086 não era um punição desagradável para programadores desavisados). Existem algumas maneiras de contornar 64k, mas a solução 8086 foi um bom compromisso.
Olof Forshell
2
@OlofForshell: Acho que muitas pessoas lamentaram o fato de que o 8086 não era tão bom quanto o 68000 (que tinha um espaço de endereçamento linear de 16 MB e um caminho livre para 4 GB). Certamente ir para um processador de 32 bits tornará mais fácil acessar mais de 64 K, mas o 8086 é uma arquitetura de 16 bits que foi projetada para ser um avanço em relação ao 8080 de 8 bits. Não vejo razão para que a Intel tenha pulado diretamente de 8 bits para 32 bits.
supercat de
3

Acho que você chegará a parte da resposta se alguma vez tentar escrever um compilador voltado para o x86, ou se escrever um emulador de máquina x86, ou mesmo se tentar implementar o ISA em um design de hardware.

Embora eu entenda o "x86 é feio!" argumentos, ainda acho mais divertido escrever assembly x86 do que MIPS (por exemplo) - o último é simplesmente tedioso. Sempre foi feito para ser bom para os compiladores, e não para os humanos. Não tenho certeza se um chip poderia ser mais hostil aos escritores de compiladores se tentasse ...

A parte mais feia para mim é a maneira como a segmentação (em modo real) funciona - que qualquer endereço físico tem 4096 segmentos: aliases de deslocamento. Quando foi a última vez que você precisou disso? As coisas teriam sido muito mais simples se a parte do segmento fosse estritamente bits de ordem superior de um endereço de 32 bits.

Bernd Jendrissek
fonte
m68k é muito mais engraçado e agradável para humanos muito mais do que x86 (que não pode parecer tão "humano" para muitos programadores m68k), se o PoV correto é a maneira como os humanos podem escrever código nesses assembly.
ShinTakezou de
O segmento: endereçamento offset foi uma tentativa de permanecer compatível, até certo ponto, com o mundo CP / M. Uma das piores decisões de todas.
Turing Complete
@Turing Complete: segment: offset NÃO foi principalmente uma tentativa de permanecer compatível com o mundo CP / M. O que foi uma tentativa muito bem-sucedida de permitir que um processador de 16 bits endereçasse mais de 64 KBytes, colocando código, dados, pilha e outras áreas de memória em segmentos diferentes.
Olof Forshell
1
Na realidade, colocar os dados e a pilha em segmentos diferentes era totalmente inútil para C; era utilizável apenas para asm. Em C, um ponteiro pode apontar para dados com duração de armazenamento estática, automática ou alocada dinamicamente, portanto, não há como omitir o segmento. Talvez fosse útil para Pascal ou Fortran ou algo assim, mas não para C, que já era a linguagem dominante na época ...
R .. GitHub PARE DE AJUDAR O ICE
2
@Bernd: A razão pela qual fs / gs foram escolhidos para armazenamento local de thread não é que os registradores de segmento sejam bons para isso. Acontece que o x86 está seriamente carente de registros e os registros de segmento não foram usados. Um registrador de propósito geral apontando para a estrutura de thread teria funcionado tão bem e, de fato, muitos sistemas RISC com mais registradores usam um como um ponteiro de thread.
R .. GitHub PARAR DE AJUDAR O ICE
1
  1. x86 tem um conjunto muito, muito limitado de registros de uso geral

  2. promove um estilo de desenvolvimento muito ineficiente no nível mais baixo (inferno CISC) em vez de uma metodologia de carregamento / armazenamento eficiente

  3. A Intel tomou a terrível decisão de introduzir o modelo de segmentação / deslocamento de memória totalmente estúpido para se manter compatível com (neste momento já!) Tecnologia desatualizada

  4. Em uma época em que todo mundo estava indo para 32 bits, o x86 atrasou o mundo dos PCs convencionais por ter apenas 16 bits (a maioria deles - o 8088 - mesmo apenas com caminhos de dados externos de 8 bits, o que é ainda mais assustador!) CPU


Para mim (e sou um veterano do DOS que viu cada geração de PCs da perspectiva dos desenvolvedores!), O ponto 3. foi o pior.

Imagine a seguinte situação que tivemos no início dos anos 90 (mainstream!):

a) Um sistema operacional que tinha limitações insanas por motivos de legado (640kB de RAM facilmente acessível) - DOS

b) Uma extensão de sistema operacional (Windows) que poderia fazer mais em termos de RAM, mas era limitada quando se tratava de coisas como jogos, etc ... e não era a coisa mais estável na Terra (felizmente isso mudou depois, mas eu estou falando sobre o início dos anos 90 aqui)

c) A maioria dos softwares ainda era DOS e tínhamos que criar discos de boot frequentemente para softwares especiais, porque havia esse EMM386.exe que alguns programas gostavam, outros odiavam (especialmente gamers - e eu era um jogador AVID nessa época - sabe o que eu estou falando aqui)

d) Estávamos limitados a bits MCGA 320x200x8 (ok, havia um pouco mais com truques especiais, 360x480x8 era possível, mas apenas sem suporte de biblioteca em tempo de execução), todo o resto estava bagunçado e horrível ("VESA" - risos)

e) Mas em termos de hardware, tínhamos máquinas de 32 bits com alguns megabytes de RAM e placas VGA com suporte de até 1024x768

O motivo dessa situação ruim?

Uma decisão de design simples da Intel. Nível de instrução da máquina (NÃO nível binário!) Compatibilidade com algo que já estava morrendo, acho que era o 8085. Os outros problemas aparentemente não relacionados (modos gráficos, etc ...) eram relacionados por razões técnicas e por causa do muito estreito arquitetura orientada que a plataforma x86 trouxe consigo.

Hoje, a situação é diferente, mas pergunte a qualquer desenvolvedor montador ou pessoas que constroem back-ends de compiladores para x86. O número insanamente baixo de registros de uso geral nada mais é do que um terrível assassino de desempenho.

Turing Completo
fonte
O único grande problema com a arquitetura segmentada do 8086 era que havia apenas um registro de segmento não dedicado (ES) e que as linguagens de programação não foram projetadas para funcionar com ele de forma eficaz. O estilo de endereçamento escalonado que ele usa funcionaria muito bem em uma linguagem orientada a objetos que não espera que os objetos sejam capazes de começar em endereços arbitrários (se alguém alinhar objetos nos limites do parágrafo, as referências de objeto precisarão ter apenas dois bytes em vez de quatro). Se compararmos o código inicial do Macintosh ao código do PC, o 8086 realmente parece muito bom em comparação com o 68000.
supercat
@supercat: na verdade, o registrador es era dedicado a algo, ou seja, àquelas instruções de string que exigiam armazenamento (movs, stos) ou digitalização (cmps e scas). Dado o endereçamento de 64 KiB de cada registrador de segmento, os es também forneceram o "elo perdido" para a memória diferente de código, dados e memória de pilha (cs, ds, ss). Os registradores de segmento forneciam uma espécie de esquema de proteção de memória, pois não era possível endereçar fora dos blocos de memória de 64Kib dos registradores. Que melhor solução você propõe, considerando que o x86 era uma arquitetura de 16 bits e as restrições de litografia da época?
Olof Forshell
@OlofForshell: ES foi usado para instruções de string, mas pode ser usado como um registro não confirmado para código que não as usa. Uma maneira de aliviar o gargalo seg-reg sem exigir muito espaço de opcode seria ter um prefixo "rseg" que especificaria que para a seguinte instrução de formato r / m o campo "r" selecionaria CS / SS / DS / ES / FS / GS / ?? / ?? em vez de AX / BX / CX / DX / SI / DI / SP / BP, e ter prefixos para FS / GS e instruções para LFS e LGS (como LDS e LES). Não sei como a microarquitetura do 8086 foi planejada, mas acho que algo assim poderia ter funcionado.
supercat
@supercat: como escrevi, "os registradores também fornecem o link que faltava para a memória além de ..." Fs e gs não chegaram até o 386, pelo que me lembro.
Olof Forshell
1
@OlofForshell: Não o fizeram, o que tornou a arquitetura 80286 ainda pior do que a arquitetura 8086 em muitos aspectos. Meu ponto é que adicionar mais alguns registradores de segmento (ou mesmo um, por falar nisso) teria tornado a arquitetura 8086 muito mais útil, e o conjunto de instruções poderia ter sido mais limpo e útil se os registradores de segmento pudessem ser acessados ​​de forma semelhante ao outros.
supercat