Dlaczego x86 jest brzydki? Dlaczego jest uważany za gorszy w porównaniu z innymi? [Zamknięte]

105

Ostatnio czytałem archiwa SO i napotkałem stwierdzenia przeciwko architekturze x86.

i wiele innych komentarzy, takich jak

Próbowałem szukać, ale nie znalazłem żadnych powodów. Prawdopodobnie nie uważam x86 za zły, ponieważ jest to jedyna architektura, którą znam.

Czy ktoś może mi uprzejmie podać powody, dla których uważam x86 za brzydkie / złe / gorsze w porównaniu z innymi.

pazury
źródło
1
Idę z S&A na podstawie dotychczasowych odpowiedzi, ale zauważę mimochodem, że CISC nie stanowi problemu dla zestawu instrukcji m68k. x86 jest tym, czym jest i możesz go zatrzymać.
dmckee --- ex-moderator kitten
co to jest „S&A”? „CISC nie stanowi problemu dla zestawu instrukcji m68k”. -- Dlaczego nie?
pazury
5
Układy scalone z serii motorala 68000 mają architekturę wysoce CISC, ale mają jednolity, dość ortogonalny i bardzo łatwy zestaw instrukcji. Skąd różnica w stosunku do x86? Nie wiem Ale zwróć uwagę, że istnieje duża różnica między złożonością chipa a złożonością zestawu instrukcji (tj. Interfejsu, który widzi programista asemblera).
dmckee --- kociak ex-moderator
4
+1 za bardzo interesujące pytanie.
Turing Ukończono
1
Niedawne badanie dotyczące efektywności energetycznej różnych procesorów, które znaleziono tutaj, z dobrą dyskusją na temat tego, co kierowało projektami CISC i RISC. extremetech.com/extreme/...

Odpowiedzi:

93

Kilka możliwych powodów:

  1. x86 to stosunkowo stary ISA (w końcu jego protoplastami były 8086)
  2. x86 ewoluował znacznie kilka razy, ale sprzęt jest wymagany do zachowania wstecznej kompatybilności ze starymi plikami binarnymi. Na przykład nowoczesny sprzęt x86 nadal obsługuje natywne uruchamianie kodu 16-bitowego. Ponadto istnieje kilka modeli adresowania pamięci, aby umożliwić starszemu kodowi współdziałanie na tym samym procesorze, na przykład tryb rzeczywisty, tryb chroniony, tryb wirtualny 8086 i tryb długi (amd64). Dla niektórych może to być mylące.
  3. x86 to maszyna CISC. Przez długi czas oznaczało to, że był wolniejszy niż maszyny RISC, takie jak MIPS lub ARM, ponieważ instrukcje mają współzależność danych i flagi, co utrudnia implementację większości form paralelizmu na poziomie instrukcji. Nowoczesne implementacje tłumaczą instrukcje x86 na instrukcje podobne do RISC, zwane „ mikrooperacjami ” pod osłonami, aby uczynić tego rodzaju optymalizacje praktycznymi do implementacji na sprzęcie.
  4. Pod pewnymi względami x86 nie jest gorszy, jest po prostu inny. Na przykład wejście / wyjście jest obsługiwane jako mapowanie pamięci w większości architektur, ale nie w x86. (Uwaga: nowoczesne maszyny x86 zazwyczaj mają jakąś formę obsługi DMA i komunikują się z innym sprzętem poprzez mapowanie pamięci; ale ISA nadal ma instrukcje I / O, takie jak INi OUT)
  5. ISA x86 ma bardzo niewiele rejestrów architektonicznych, co może wymuszać na programach częstsze przemieszczanie się w obie strony przez pamięć, niż byłoby to konieczne w innym przypadku. Dodatkowe instrukcje potrzebne do tego wymagają zasobów wykonawczych, które można by wykorzystać na użyteczną pracę, chociaż wydajne przekazywanie do magazynuutrzymuje niskie opóźnienie. Nowoczesne implementacje ze zmianą nazwy rejestrów na duży plik rejestru fizycznego mogą utrzymywać wiele instrukcji w locie, ale brak rejestrów architektonicznych był nadal znaczącą słabością 32-bitowego x86. Wzrost x86-64 z 8 do 16 rejestrów całkowitych i wektorów jest jednym z największych czynników wpływających na to, że kod 64-bitowy jest szybszy niż 32-bitowy (wraz z bardziej wydajnym wywołaniem rejestru ABI), a nie zwiększona szerokość każdego rejestru. Dalszy wzrost z 16 do 32 rejestrów całkowitych pomógłby niektórym, ale nie tak bardzo. (AVX512 jednak zwiększa się do 32 rejestrów wektorowych, ponieważ kod zmiennoprzecinkowy ma większe opóźnienie i często potrzebuje więcej stałych.) ( Patrz komentarz )
  6. Kod asemblera x86 jest skomplikowany, ponieważ x86 to skomplikowana architektura z wieloma funkcjami. Lista instrukcji dla typowej maszyny MIPS mieści się na kartce papieru o rozmiarze jednej litery. Równoważna lista dla x86 wypełnia kilka stron, a instrukcje po prostu robią więcej, więc często potrzebujesz większego wyjaśnienia tego, co robią, niż może dostarczyć lista. Na przykład MOVSBinstrukcja wymaga stosunkowo dużego bloku kodu C, aby opisać, co robi:

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

    To jest pojedyncza instrukcja wykonująca ładowanie, zapisująca i dwa dodania lub odejmowania (kontrolowane przez wejście flagi), z których każda byłaby oddzielną instrukcją na maszynie RISC.

    Chociaż prostota MIPS (i podobnych architektur) niekoniecznie czyni je lepszymi, do nauczania wprowadzenia do klasy asemblera sensowne jest rozpoczęcie od prostszego ISA . Niektóre klasy asemblera uczą ultra-uproszczonego podzbioru x86 zwanego y86 , który jest uproszczony poza tym, że nie jest przydatny w prawdziwym użyciu (np. Bez instrukcji zmiany biegów), lub niektóre uczą tylko podstawowych instrukcji x86.

  7. X86 używa kodów operacyjnych o zmiennej długości, które zwiększają złożoność sprzętową w odniesieniu do analizowania instrukcji. We współczesnej erze ten koszt staje się znikomo mały, ponieważ procesory są coraz bardziej ograniczane przez przepustowość pamięci niż przez surowe obliczenia, ale wiele artykułów i postaw dotyczących "walenia x86" pochodzi z czasów, gdy koszt ten był znacznie wyższy.
    Aktualizacja 2016: Anandtech opublikował dyskusję dotyczącą rozmiarów kodów operacyjnych w wersjach x64 i AArch64 .

EDYCJA: To nie ma być bash x86! przyjęcie. Nie miałem innego wyboru, jak tylko trochę bić, biorąc pod uwagę sposób sformułowania pytania. Ale z wyjątkiem (1), wszystkie te rzeczy zostały zrobione z ważnych powodów (patrz komentarze). Projektanci Intela nie są głupi - chcieli coś osiągnąć dzięki swojej architekturze, a to tylko niektóre z podatków, które musieli zapłacić, aby te rzeczy stały się rzeczywistością.

Billy ONeal
źródło
17
To kompromis. Jego zaletą jest to, że rozmiar binarny może być mniejszy, ale jego wadą jest to, że do zaimplementowania parsera dla tych instrukcji potrzebny jest bardzo skomplikowany sprzęt. Zdecydowana większość instrukcji i tak ma ten sam rozmiar - większość powodów dla zmiennych długości kodów na x86 polega na tym, że zdecydowali się dodać funkcje i stwierdzili, że nie mogą przedstawić tego, czego chcieli, w liczbie bitów, z którymi musieli pracować . Zdecydowana większość ludzi nie przejmuje się rozmiarem binarnym prawie tak bardzo, jak złożonością sprzętu lub zużyciem energii.
Billy ONeal
8
@Joey Adams: Porównaj instrukcje zmiennej długości x86 z trybem kciuka ARM ( en.wikipedia.org/wiki/ARM_architecture#Thumb ). Tryb kciuka skutkuje znacznie mniejszym kodem obiektowym dla ARM, ponieważ krótsze instrukcje mapują bezpośrednio na normalne instrukcje. Ale ponieważ istnieje mapowanie 1: 1 między większymi instrukcjami a mniejszymi, sprzęt analizujący jest prosty do zaimplementowania. Instrukcje o zmiennej długości x86 nie mają tych zalet, ponieważ nie zostały zaprojektowane w ten sposób.
Billy ONeal
7
(6) Nie każdy kod operacyjny musi być używany przez każdy program, ale do cholery, kiedy potrzebuję SSE3, cieszę się, że go mam.
Chris K
4
@Chris Kaminski: Jak to nie wpływa na sprzęt? Jasne, na nowoczesnym pełnowymiarowym komputerze nikogo to nie obchodzi, ale jeśli robię coś takiego jak telefon komórkowy, bardziej zależy mi na zużyciu energii niż na prawie wszystkim innym. Kody o zmiennej długości nie zwiększają czasu wykonywania, ale sprzęt dekodujący nadal wymaga zasilania do działania.
Billy ONeal
5
Co jest jedną z rzeczy, które sprawiają, że zestaw instrukcji x86 jest tak brzydki, ponieważ nie może zdecydować, czy jest to architektura oparta na akumulatorze czy plikach rejestru (chociaż zostało to w większości naprawione w 386, co sprawiło, że zestaw instrukcji był znacznie bardziej ortogonalny , niezależnie od tego, co mówią fani 68k).
ninjalj
25

Moim zdaniem głównym uderzeniem w x86 jest jego pochodzenie CISC - zestaw instrukcji zawiera wiele ukrytych współzależności. Te współzależności utrudniają wykonywanie takich czynności, jak zmiana kolejności instrukcji na chipie, ponieważ artefakty i semantyka tych współzależności muszą być zachowane dla każdej instrukcji.

Na przykład, większość instrukcji dodawania i odejmowania liczb całkowitych x86 modyfikuje rejestr flag. Po wykonaniu dodawania lub odejmowania, następną operacją jest często sprawdzenie rejestru flag w celu sprawdzenia przepełnienia, bitu znaku itp. Jeśli po tym jest kolejny dodatek, bardzo trudno jest stwierdzić, czy można bezpiecznie rozpocząć wykonywanie drugiego dodawania zanim wynik pierwszego dodania będzie znany.

W architekturze RISC instrukcja add określałaby operandy wejściowe i rejestry wyjściowe, a wszystko, co dotyczy operacji, odbywałoby się przy użyciu tylko tych rejestrów. To znacznie ułatwia oddzielanie operacji dodawania, które są blisko siebie, ponieważ nie ma żadnego rejestru flag, które zmuszają wszystko do wyrównania i wykonania pojedynczego pliku.

Chip DEC Alpha AXP, projekt RISC w stylu MIPS, był boleśnie spartański w dostępnych instrukcjach, ale zestaw instrukcji został zaprojektowany tak, aby uniknąć niejawnych zależności rejestrów między instrukcjami. Nie było rejestru stosu zdefiniowanego sprzętowo. Nie było rejestru flag zdefiniowanych sprzętowo. Nawet wskaźnik instrukcji był zdefiniowany w systemie operacyjnym - jeśli chciałeś wrócić do dzwoniącego, musiałeś dowiedzieć się, w jaki sposób dzwoniący poinformuje Cię, na który adres powrócić. Było to zwykle definiowane przez konwencję wywoływania systemu operacyjnego. Jednak na x86 jest to definiowane przez sprzęt chipowy.

W każdym razie, ponad 3 lub 4 generacje układów Alpha AXP, sprzęt przeszedł od bycia dosłowną implementacją spartańskiego zestawu instrukcji z 32 rejestrami int i 32 rejestrami float do ogromnie niedziałającego silnika realizacji zleceń z 80 wewnętrznymi rejestrami, zmiana nazwy rejestrów, przekazywanie wyniku (gdzie wynik poprzedniej instrukcji jest przekazywany do późniejszej instrukcji zależnej od wartości) i wszelkiego rodzaju dzikie i szalone wzmacniacze wydajności. Z tymi wszystkimi dzwonkami i gwizdkami, układ scalony AXP był nadal znacznie mniejszy niż porównywalny układ Pentium z tamtych czasów, a AXP był o wiele szybszy.

W drzewie genealogicznym x86 nie widać takich wybuchów zwiększających wydajność, głównie dlatego, że złożoność zestawu instrukcji x86 sprawia, że ​​wiele rodzajów optymalizacji wykonywania jest zbyt kosztownych, jeśli nie niemożliwych. Geniusz Intela polegał na rezygnacji z implementacji zestawu instrukcji x86 w sprzęcie - wszystkie nowoczesne chipy x86 są w rzeczywistości rdzeniami RISC, które do pewnego stopnia interpretują instrukcje x86, tłumacząc je na wewnętrzny mikrokod, który zachowuje całą semantykę oryginalnego x86 instrukcji, ale pozwala na trochę niedziałającego RISC i innych optymalizacji w mikrokodzie.

Napisałem dużo asemblera x86 i potrafię w pełni docenić wygodę jego korzeni CISC. Ale nie doceniałem w pełni, jak skomplikowany jest x86, dopóki nie spędziłem trochę czasu na pisaniu asemblera Alpha AXP. Zaskoczyła mnie prostota i jednolitość AXP. Różnice są ogromne i głębokie.

dthorpe
źródło
6
Nie będę słuchał walenia w CISC per se, chyba że wyjaśnisz m68k.
dmckee --- ex-moderator kitten
2
Nie znam m68k, więc nie mogę go krytykować.
dthorpe
4
Nie sądzę, aby ta odpowiedź była na tyle zła, aby głosować przeciw, ale uważam, że cały argument „RISC jest mniejszy i szybszy niż CISC” nie jest tak naprawdę istotny we współczesnej erze. Jasne, AXP mógł być o wiele szybszy jak na swój czas, ale faktem jest, że nowoczesne RISC i nowoczesne CISC są mniej więcej takie same, jeśli chodzi o wydajność. Jak powiedziałem w mojej odpowiedzi, niewielki spadek mocy przy dekodowaniu x86 jest powodem, dla którego nie należy używać x86 do czegoś takiego jak telefon komórkowy, ale to mały argument dla pełnowymiarowego komputera stacjonarnego lub notebooka.
Billy ONeal
4
@Billy: rozmiar to coś więcej niż tylko rozmiar kodu czy rozmiar instrukcji. Intel płaci sporą karę za powierzchnię chipa za implementację logiki sprzętowej dla wszystkich tych specjalnych instrukcji, niezależnie od tego, czy rdzeń mikrokodu RISC jest pod maską, czy nie. Rozmiar matrycy ma bezpośredni wpływ na koszt produkcji, więc jest to nadal ważny problem w przypadku nowoczesnych projektów systemów.
dthorpe
1
@dthorpe: Nie zgadzam się z większością, jeśli nie ze wszystkimi, z tego, co napisałeś. Od czasu 8086 nie trzeba było się martwić, czy można bezpiecznie wykonać addnastępny add. Zasady są jasne. Nie ma również potrzeby zajmowania się zmianą kolejności instrukcji. Od czasu Pentium Pro w połowie lat 90. procesor robi to za Ciebie. To, o czym wspominasz, mogło być problemem 20 lat temu, ale obecnie nie widzę powodu, aby mieć to przeciwko architekturze x86.
Nathan Fellman
21

Architektura x86 pochodzi z projektu mikroprocesora 8008 i pokrewnych. Te procesory zostały zaprojektowane w czasach, gdy pamięć była wolna i jeśli można było to zrobić na matrycy procesora, często była o wiele szybsza. Jednak przestrzeń matrycy procesora była również droga. Te dwa powody powodują, że istnieje tylko niewielka liczba rejestrów, które mają zwykle specjalne przeznaczenie, oraz skomplikowany zestaw instrukcji z różnego rodzaju pułapkami i ograniczeniami.

Inne procesory z tej samej epoki (np. Rodzina 6502) również mają podobne ograniczenia i dziwactwa. Co ciekawe, zarówno seria 8008, jak i seria 6502 były przeznaczone jako kontrolery wbudowane. Już wtedy oczekiwano, że wbudowane kontrolery będą programowane w asemblerze i pod wieloma względami przeznaczone raczej dla programisty asemblera, a nie dla kompilatora. (Spójrz na chip VAX, aby zobaczyć, co się stanie, gdy zajmiesz się pisaniem kompilatora). Projektanci nie spodziewali się, że staną się platformami komputerowymi ogólnego przeznaczenia; po to były takie rzeczy, jak poprzednicy architektury POWER. Rewolucja komputerów domowych zmieniła to oczywiście.

staticsan
źródło
4
+1 za jedyną odpowiedź od kogoś, kto wydaje się mieć tło historyczne w tej sprawie.
Billy ONeal
3
Pamięć zawsze była powolna. Prawdopodobnie (względnie rzecz biorąc) jest dziś wolniejsze niż wtedy, gdy zaczynałam od Z80 i CP / M w 1982 roku. Wymieranie nie jest jedyną ścieżką ewolucji, ponieważ wraz z wyginięciem ten konkretny kierunek ewolucji się zatrzymuje. Powiedziałbym, że x86 dobrze się przystosował w ciągu 28 lat (do tej pory istnienia).
Olof Forshell,
4
Szybkość pamięci osiągnęła na krótko prawie parzystość z procesorami w czasach 8086. 9900 firmy Texas Instruments ma konstrukcję, która działa tylko dlatego, że tak się stało. Ale potem procesor ponownie ruszył do przodu i tam został. Dopiero teraz dostępne są pamięci podręczne, które pomagają w zarządzaniu tym.
staticsan
3
@Olof Forshell: Był kompatybilny z asemblerem, ponieważ kod assemblera 8080 mógł przełożyć na kod 8086. Z tego punktu widzenia było to 8080 plus rozszerzeń, podobnie jak można było zobaczyć 8080 jako 8008 plus rozszerzenia.
David Thornley
3
@Olof Forshell: Tyle że 8086 został zaprojektowany do tego celu. Było to rozszerzenie 8080, a większość (prawdopodobnie wszystkie) instrukcji 8080 odwzorowywanych jeden na jeden, z oczywiście podobną semantyką. Nie jest to prawdą w przypadku architektury IBM 360, bez względu na to, w jaki sposób chcesz ją popchnąć.
David Thornley
13

Mam tutaj kilka dodatkowych aspektów:

Rozważmy operację „a = b / c” x86 zaimplementowałaby to jako

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

Jako dodatkowy bonus instrukcji div, edx będzie zawierał resztę.

Procesor RISC wymagałby najpierw załadowania adresów b i c, załadowania bic z pamięci do rejestrów, wykonania dzielenia i załadowania adresu a, a następnie zapamiętania wyniku. Dst, składnia 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

Tutaj zazwyczaj nie będzie reszty.

Jeśli jakiekolwiek zmienne mają być ładowane przez wskaźniki, obie sekwencje mogą stać się dłuższe, chociaż jest to mniej prawdopodobne dla RISC, ponieważ może mieć jeden lub więcej wskaźników już załadowanych w innym rejestrze. x86 ma mniej rejestrów, więc prawdopodobieństwo znalezienia się wskaźnika w jednym z nich jest mniejsze.

Plusy i minusy:

Instrukcje RISC mogą być mieszane z otaczającym kodem w celu usprawnienia planowania instrukcji, jest to mniej prawdopodobne w przypadku x86, który zamiast tego wykonuje tę pracę (mniej lub bardziej dobrze w zależności od sekwencji) wewnątrz samego procesora. Powyższa sekwencja RISC będzie miała zazwyczaj długość 28 bajtów (7 instrukcji 32-bitowych / 4 bajty każda) w architekturze 32-bitowej. Spowoduje to, że pamięć zewnętrzna będzie pracować więcej podczas pobierania instrukcji (siedem pobrań). Gęstsza sekwencja x86 zawiera mniej instrukcji i chociaż ich szerokości są różne, prawdopodobnie patrzysz tam również na średnio 4 bajty / instrukcję. Nawet jeśli masz pamięci podręczne instrukcji, aby przyspieszyć to, siedem pobrań oznacza, że ​​będziesz miał deficyt trzech w innym miejscu do nadrobienia w porównaniu z x86.

Architektura x86 z mniejszą liczbą rejestrów do zapisywania / odtwarzania oznacza, że ​​prawdopodobnie będzie wykonywać przełączanie wątków i obsługiwać przerwania szybciej niż RISC. Więcej rejestrów do zapisania i przywrócenia wymaga więcej tymczasowego miejsca w stosie pamięci RAM do wykonywania przerwań i bardziej trwałego miejsca w stosie do przechowywania stanów wątków. Te aspekty powinny uczynić x86 lepszym kandydatem do uruchamiania czystego systemu RTOS.

Z bardziej osobistego punktu widzenia, trudniej jest napisać asembler RISC niż x86. Rozwiązuję to, pisząc procedurę RISC w C, kompilując i modyfikując wygenerowany kod. Jest to bardziej wydajne z punktu widzenia produkcji kodu i prawdopodobnie mniej wydajne z punktu widzenia wykonania. Wszystkie te 32 rejestry do śledzenia. W przypadku x86 jest odwrotnie: 6-8 rejestrów z „prawdziwymi” nazwami sprawia, że ​​problem jest łatwiejszy do opanowania i daje większą pewność, że wyprodukowany kod będzie działał zgodnie z oczekiwaniami.

Brzydki? To jest w oku patrzącego. Wolę „inny”.

Olof Forshell
źródło
a, b i c w moich przykładach powinny być postrzegane jako zmienne oparte na pamięci, a nie jako wartości bezpośrednie.
Olof Forshell,
... "dword ptr" służy do określenia rozmiaru zmiennej, której rozmiar nie jest znany, jeśli na przykład jest po prostu zadeklarowana jako zewnętrzna lub jeśli byłeś leniwy.
Olof Forshell
2
To nie pierwszy raz, kiedy usłyszałem sugestię, aby najpierw napisać to w C, a następnie przelać do asemblera. To zdecydowanie pomaga
Joe Plante
Na początku wszystkie procesory były RISC. CISC pojawił się jako strategia łagodzenia skutków dla systemów pamięci z rdzeniem żelaznym, które były BARDZO powolne, a zatem CISC, z mniejszą liczbą potężniejszych instrukcji, obciążył podsystem pamięci mniejszym obciążeniem i lepiej wykorzystywał przepustowość. Podobnie, rejestry pierwotnie uważano za wbudowane w procesor, lokalizacje pamięci do wykonywania akumulacji. Ostatni raz na poważnie testowałem maszynę RISC w 1993 roku - SPARC i HP Prisim. Pod każdym względem SPARC był okropny. Prisim był do 20 razy szybszy niż 486 na add / sub / mul, ale był do niczego. CISC jest lepsze.
@OlofForshell Mówisz, there typically won't be a reminderale wiki mówi, że mips ma to: en.wikipedia.org/wiki/MIPS_instruction_set#Integer
Alex Zhukovskiy
10

Myślę, że to pytanie ma fałszywe założenie. To głównie akademicy z obsesją na punkcie RISC nazywają x86 brzydkim. W rzeczywistości ISA x86 może wykonać w ramach jednej instrukcji operacje, które zajęłyby 5-6 instrukcji na ISA RISC. Wentylatory RISC mogą przeciwdziałać temu, że nowoczesne procesory x86 rozbijają te „złożone” instrukcje na mikroopisy; jednak:

  1. W wielu przypadkach jest to prawdą tylko częściowo lub wcale. Najbardziej użyteczne „złożone” instrukcje w x86 to mov %eax, 0x1c(%esp,%edi,4)np. Tryby adresowania i nie są one podzielone.
  2. Często ważniejsza na nowoczesnych maszynach nie jest liczba spędzonych cykli (ponieważ większość zadań nie jest związana z procesorem), ale wpływ kodu na pamięć podręczną instrukcji. 5-6 instrukcji o stałym rozmiarze (zwykle 32-bitowych) wpłynie na pamięć podręczną o wiele więcej niż jedna złożona instrukcja, która rzadko przekracza 5 bajtów.

x86 naprawdę wchłonął wszystkie dobre aspekty RISC około 10-15 lat temu, a pozostałe cechy RISC (a właściwie ta definiująca - minimalny zestaw instrukcji) są szkodliwe i niepożądane.

Pomijając koszt i złożoność produkcji procesorów oraz ich wymagania energetyczne, x86 jest najlepszym ISA . Każdy, kto mówi ci inaczej, pozwala, by ideologia lub program przeszkadzały mu w rozumowaniu.

Z drugiej strony, jeśli celujesz w urządzenia wbudowane, w których liczy się koszt procesora, lub urządzenia wbudowane / mobilne, w których zużycie energii jest głównym problemem, prawdopodobnie ARM lub MIPS mają większy sens. Pamiętaj jednak, że nadal będziesz musiał radzić sobie z dodatkową pamięcią RAM i rozmiarem binarnym potrzebnym do obsługi kodu, który jest z łatwością 3-4 razy większy i nie będziesz w stanie zbliżyć się do wydajności. To, czy to ma znaczenie, zależy w dużej mierze od tego, co na nim będziesz biegać.

R .. GitHub PRZESTAŃ POMÓC LODOM
źródło
3
gdzie zużycie energii jest głównym problemem, ARM lub MIPS prawdopodobnie ma większy sens ... więc jeśli jest przynajmniej jeden aspekt, w którym ARM lub MIPS mają więcej sensu, czy nie czyni to x86 niekoniecznie najlepszym ISA?
Shahbaz,
Dlatego określiłem „najlepszych” słowami „oprócz kosztów… i ich zapotrzebowania na energię”.
R .. GitHub STOP HELPING ICE
1
Myślę, że zmniejszenie szybkości procesora przez Intela i mniejsze rozmiary matryc w znacznym stopniu wyeliminowały różnicę mocy. Nowy podwójny 64-bitowy procesor Celeron z 64 tys. Pamięci podręcznych L1 i 1 MB pamięci podręcznej L2 to układ o mocy 7,5 W. To moja maszyna do spotkań „Starbucks”, a żywotność baterii jest absurdalnie długa i będzie działać w kółko wokół maszyny P6. Jako facet wykonujący głównie obliczenia zmiennoprzecinkowe dawno porzuciłem RISC. Po prostu czołga się. Szczególnie SPARC był potwornie lodowaty. Doskonałym przykładem na to, dlaczego RISC jest do bani, był procesor Intel i860. Intel nigdy więcej TAM NIE Wróciła.
@RocketRoy: 7,5 wata nie jest do przyjęcia dla urządzenia, które jest zasilane 24 godziny na dobę, 7 dni w tygodniu (i przez cały czas nie wykonuje przydatnych obliczeń) lub działa z akumulatorem 3,7 V / 2000 mAh.
R .. GitHub STOP HELPING ICE
2
@RocketRoy "Procesor Intel i860. Intel nigdy więcej TAM NIE wrócił." Po krótkich badaniach i860 brzmi bardzo podobnie do Itanium: VLIW, równoległość instrukcji uporządkowana przez kompilator ...
Jonathon Reinhart
9

Język asemblera x86 nie jest taki zły. Kiedy dotrzesz do kodu maszynowego, zaczyna on być naprawdę brzydki. Kodowanie instrukcji, tryby adresowania itp. Są znacznie bardziej skomplikowane niż w przypadku większości procesorów RISC. Jest też wbudowana dodatkowa zabawa dla celów kompatybilności wstecznej - rzeczy, które działają tylko wtedy, gdy procesor jest w określonym stanie.

Na przykład w trybach 16-bitowych adresowanie może wydawać się wręcz dziwaczne; istnieje tryb adresowania dla [BX+SI], ale nie dla [AX+BX]. Takie rzeczy zwykle komplikują użycie rejestru, ponieważ musisz upewnić się, że twoja wartość znajduje się w rejestrze, którego możesz używać w razie potrzeby.

(Na szczęście tryb 32-bitowy jest znacznie rozsądniejszy (choć czasami wciąż jest nieco dziwny - na przykład segmentacja), a 16-bitowy kod x86 jest już w dużej mierze nieistotny poza programami ładującymi i niektórymi środowiskami osadzonymi.)

Są też pozostałości z dawnych czasów, kiedy Intel próbował uczynić x86 najlepszym procesorem. Instrukcje o długości kilku bajtów, które wykonywały zadania, których nikt już nie wykonuje, ponieważ, szczerze mówiąc, były zbyt powolne lub skomplikowane. Instrukcje ENTER i LOOP , dla dwóch przykładów - zauważ, że kod ramki stosu C jest podobny do „push ebp; mov ebp, esp”, a nie „enter” dla większości kompilatorów.

cHao
źródło
2
Uważam, że problem „enter” i „push / mov” powstał, ponieważ na niektórych procesorach „push / mov” jest szybsze. W przypadku niektórych procesorów „enter” jest szybsze. C'est la vie.
Dietrich Epp
4
Kiedy byłem zmuszony do korzystania z maszyny opartej na x86 i zacząłem się temu przyglądać (mając tło m68k), zacząłem czuć się frustrującym programowaniem asm, ... jak gdybym nauczył się programowania w języku takim jak C, a potem był zmuszony do kontaktu z asm… czujesz, że tracisz siłę ekspresji, łatwość, klarowność, „spójność”, „intuicyjność”. Jestem pewien, że gdybym zaczął programować asm z x86, pomyślałbym nie jest tak źle ... może ... zrobiłem też MMIX i MIPS, a ich "asm lang" jest o wiele lepszy niż x86 (jeśli to jest właściwy PoV dla Q, ale może nie)
ShinTakezou
Problem trybu adresowania został rozwiązany w 80386. Tylko kod 16-bitowy ma ograniczone tryby adresowania, kod 32-bitowy jest znacznie lepszy. Możesz uzyskać 32-bitowe tryby adresowania w 16-bitowym kodzie za pomocą specjalnego prefiksu i odwrotnie.
fuz
@FUZxxl: Tak ... prawdopodobnie powinienem był wspomnieć, że brzydota ogranicza się głównie do kodu 16-bitowego. Naprawiono (myślę). :)
cHao
Postrzegana nieelegancja wynika głównie z błędnego przekonania, że ​​rejestry 8086 są rejestrami ogólnego przeznaczenia; to jest niepoprawne. Każdy z nich ma specjalne przeznaczenie i jeśli nie będziesz się ich trzymał, będziesz miał zły czas.
fuz
3

Nie jestem ekspertem, ale wydaje się, że wiele funkcji, które ludziom nie podobają się, może być powodem, dla których działa dobrze. Kilka lat temu posiadanie rejestrów (zamiast stosu), ramek rejestrów itp. Było postrzegane jako dobre rozwiązanie, dzięki któremu architektura wydawała się ludziom prostsza. Jednak w dzisiejszych czasach liczy się wydajność pamięci podręcznej, a słowa o zmiennej długości x86 pozwalają na przechowywanie większej liczby instrukcji w pamięci podręcznej. „Dekodowanie instrukcji”, które, jak sądzę, wskazali przeciwnicy, kiedyś zajęło połowę chipa, już nie jest takie bardzo.

Myślę, że równoległość jest obecnie jednym z najważniejszych czynników - przynajmniej w przypadku algorytmów, które działają już wystarczająco szybko, aby można je było wykorzystać. Wyrażenie wysokiego paralelizmu w oprogramowaniu umożliwia sprzętowi amortyzację (lub często całkowite ukrycie) opóźnień pamięci. Oczywiście dalsza przyszłość architektury jest prawdopodobnie związana z komputerami kwantowymi.

Słyszałem od nVidii, że jednym z błędów Intela było to, że trzymali formaty binarne blisko sprzętu. PTX CUDA wykonuje kilka szybkich obliczeń użycia rejestrów (kolorowanie grafów), więc nVidia może używać maszyny rejestrującej zamiast maszyny stosowej, ale nadal ma ścieżkę aktualizacji, która nie psuje całego starego oprogramowania.

gatoatigrado
źródło
9
RISC nie został zaprojektowany z myślą o ludzkich programistach. Jednym z pomysłów stojących za RISC było przeładowanie części złożoności chipa na każdego, kto napisał asembler, najlepiej na kompilator. Więcej rejestrów oznaczało mniejsze zużycie pamięci i mniej zależności między instrukcjami, umożliwiając głębsze potoki i wyższą wydajność. Zauważ, że x86-64 ma dwa razy więcej rejestrów ogólnych niż x86 i samo to jest odpowiedzialne za znaczny wzrost wydajności. A instrukcje na większości układów x86 są dekodowane przed zapisaniem ich w pamięci podręcznej, a nie po (więc rozmiar nie ma tutaj znaczenia).
Dietrich Epp
3
@Dietrich Epp: To nie do końca prawda. Procesory x86-64 mają więcej rejestrów widocznych w ISA, ale nowoczesne implementacje x86 zwykle mają plik rejestru w stylu RISC, który jest mapowany do rejestrów ISA na żądanie w celu przyspieszenia wykonania.
Billy ONeal
„Słyszałem od firmy nVidia, że ​​jednym z błędów Intela było utrzymywanie formatów binarnych blisko sprzętu”. - Nie dostałem tego i części PTX CUDA.
pazury
1
@Dietrech Epp: „A instrukcje na większości chipów x86 są dekodowane przed umieszczeniem ich w pamięci podręcznej, a nie po”. To nieprawda. Są buforowane przed odkodowaniem. Uważam, że Pentium 4 miał dodatkową pamięć podręczną śledzenia, która była buforowana po zdekodowaniu, ale została przerwana.
Nathan Fellman
to nieprawda, najnowsze procesory "piaszczystego mostu" używają czegoś w rodzaju pamięci podręcznej śladów (jak w przypadku Pentium 4, o stary chłopcze: D), więc technologie znikają i wracają ...
Quonux
3

Oprócz powodów, o których ludzie już wspomnieli:

  • x86-16 miał dość dziwny schemat adresowania pamięci, który pozwalał na adresowanie pojedynczego miejsca pamięci na 4096 różnych sposobów, ograniczał RAM do 1 MB i zmuszał programistów do radzenia sobie z dwoma różnymi rozmiarami wskaźników. Na szczęście przejście na wersję 32-bitową sprawiło, że ta funkcja stała się niepotrzebna, ale chipy x86 nadal mają okruchy rejestrów segmentowych.
  • Chociaż nie jest to wina x86 per se , x86 konwencje telefoniczne nie były standaryzowane jak MIPS był (głównie dlatego, MS-DOS nie pochodzą z żadnej kompilatorów), pozostawiając nas z nieporządku __cdecl, __stdcall, __fastcall, itd.
dan04
źródło
Hmm ... kiedy myślę o konkurentach x86, nie myślę o MIPS. ARM lub PowerPC może ...
Billy ONeal
@Billy: x86 istnieje prawie od zawsze. Kiedyś MIPS był konkurentem x86. O ile pamiętam, x86 wykonało swoją pracę, aby osiągnąć poziom, na którym konkurował z MIPS. (Kiedy MIPS i SPARC walczyły na arenie stacji roboczych.)
Shannon Severance,
@Shannon Severance: To, że coś kiedyś było, nie oznacza, że ​​jest.
Billy ONeal
2
@supercat: ludzie w dobie płaskiego modelu pamięci x86-32 zwykle zapominają, że 16 bitów oznacza 64k pamięci (każdy, kto zawraca sobie głowę matematyką, zrozumie, że magia nie jest możliwa, że ​​8086 nie był nieprzyjemna kara dla niczego niepodejrzewających programistów). Jest kilka sposobów na obejście 64k, ale rozwiązanie 8086 było dobrym kompromisem.
Olof Forshell
2
@OlofForshell: Myślę, że wiele osób opłakiwało fakt, że 8086 nie był tak ładny jak 68000 (który miał 16MB liniowej przestrzeni adresowej i wyraźną ścieżkę do 4 koncertów). Z pewnością przejście na 32-bitowy procesor ułatwi dostęp do więcej niż 64K, ale 8086 to architektura 16-bitowa, która została zaprojektowana jako krok naprzód w stosunku do 8-bitowego 8080. Nie widzę powodu, dla którego Intel miałby skakać bezpośrednio z wersji 8-bitowej na 32-bitową.
supercat
3

Myślę, że dojdziesz do części odpowiedzi, jeśli kiedykolwiek spróbujesz napisać kompilator, który jest przeznaczony dla x86, lub jeśli napiszesz emulator maszyny x86, lub nawet jeśli spróbujesz zaimplementować ISA w projekcie sprzętu.

Chociaż rozumiem, że „x86 jest brzydki!” argumenty, nadal uważam, że pisanie asemblera x86 jest fajniejsze niż MIPS (na przykład) - ten ostatni jest po prostu żmudny. Zawsze miało być miłe dla kompilatorów, a nie dla ludzi. Nie jestem pewien, czy chip mógłby być bardziej wrogi dla autorów kompilatorów, gdyby spróbował ...

Najbrzydszą częścią dla mnie jest sposób działania segmentacji (w trybie rzeczywistym) - że każdy adres fizyczny ma aliasy segment: przesunięcie 4096. Kiedy ostatnio tego potrzebowałeś ? Sytuacja byłaby o wiele prostsza, gdyby część segmentowa stanowiła bity ściśle wyższego rzędu 32-bitowego adresu.

Bernd Jendrissek
źródło
m68k jest o wiele zabawniejszy i miły dla ludzi o wiele bardziej niż x86 (co nie może wydawać się tak „ludzkie” dla wielu programistów m68k), jeśli właściwy PoV jest sposobem, w jaki człowiek może pisać kod w tym assemblerze.
ShinTakezou
Segment: adresowanie offsetowe było próbą pozostania w pewnym stopniu kompatybilnym ze światem CP / M. Jedna z najgorszych decyzji w historii.
Turing Ukończono
@Turing Complete: segment: offset NIE był przede wszystkim próbą zachowania kompatybilności ze światem CP / M. To była bardzo udana próba umożliwienia 16-bitowemu procesorowi adresowania ponad 64 KB poprzez umieszczenie kodu, danych, stosu i innych obszarów pamięci w różnych segmentach.
Olof Forshell,
1
W rzeczywistości umieszczanie danych i stosu w różnych segmentach było całkowicie bezużyteczne dla C; było to użyteczne tylko dla asm. W języku C wskaźnik może wskazywać na dane ze statycznym, automatycznym lub dynamicznie przydzielanym czasem trwania, więc nie ma możliwości usunięcia segmentu. Może to było przydatne dla Pascala, Fortrana czy czegoś podobnego, ale nie dla C, który był już wtedy dominującym językiem ...
R .. GitHub STOP HELPING ICE
2
@Bernd: Powodem, dla którego wybrano fs / gs do przechowywania lokalnego wątku nie jest to, że rejestry segmentowe są do tego dobre. Po prostu x86 jest poważnie zagłodzony dla rejestrów, a rejestry segmentów były nieużywane. Rejestr ogólnego przeznaczenia wskazujący na strukturę wątku działałby równie dobrze, aw rzeczywistości wiele systemów RISC z większą liczbą rejestrów używa jednego jako wskaźnika wątku.
R .. GitHub STOP HELPING ICE
1
  1. x86 ma bardzo, bardzo ograniczony zestaw rejestrów ogólnego przeznaczenia

  2. promuje bardzo nieefektywny styl rozwoju na najniższym poziomie (piekło CISC) zamiast efektywnej metodologii ładowania / przechowywania

  3. Intel podjął przerażającą decyzję o wprowadzeniu po prostu głupiego segmentu / przesunięcia - model adresowania pamięci, aby pozostać kompatybilnym z (już teraz!) Przestarzałą technologią

  4. W czasach, gdy wszyscy korzystali z 32-bitowych, x86 powstrzymywał główny świat PC, będąc skromnym 16-bitowym (większość z nich - 8088 - nawet tylko z 8-bitowymi zewnętrznymi ścieżkami danych, co jest jeszcze bardziej przerażające!)


Dla mnie (a jestem weteranem DOS, który widział każdą generację komputerów PC z punktu widzenia programistów!) Punkt 3. był najgorszy.

Wyobraź sobie następującą sytuację, jaką mieliśmy na początku lat 90. (mainstream!):

a) System operacyjny, który miał szalone ograniczenia z powodów starszych (640kB łatwo dostępnej pamięci RAM) - DOS

b) Rozszerzenie systemu operacyjnego (Windows), które mogło zrobić więcej w zakresie pamięci RAM, ale było ograniczone, jeśli chodzi o rzeczy takie jak gry itp. ... i nie było najbardziej stabilną rzeczą na Ziemi (na szczęście zmieniło się to później, ale ja mówię tutaj o wczesnych latach 90-tych)

c) Większość oprogramowania była nadal DOS-em i musieliśmy często tworzyć dyski startowe dla specjalnego oprogramowania, ponieważ był taki EMM386.exe, który niektóre programy lubiły, inne nienawidziły (zwłaszcza gracze - a ja byłem wtedy graczem AVID - wiesz, co ja) mówię o tym)

d) Byliśmy ograniczeni do bitów MCGA 320x200x8 (ok, było trochę więcej ze specjalnymi sztuczkami, 360x480x8 było możliwe, ale tylko bez obsługi biblioteki wykonawczej), wszystko inne było niechlujne i okropne ("VESA" - lol)

e) Ale jeśli chodzi o sprzęt, mieliśmy maszyny 32-bitowe z kilkoma megabajtami pamięci RAM i kartami VGA z obsługą do 1024x768

Powód tej złej sytuacji?

Prosta decyzja projektowa firmy Intel. Zgodność poziomu instrukcji maszyny (NIE poziom binarny!) Z czymś, co już umierało, myślę, że był to 8085. Inne, pozornie niezwiązane problemy (tryby graficzne, itp.) Były związane z przyczyn technicznych i z powodu bardzo wąskiego świadomą architekturę, którą platforma x86 przyniosła ze sobą.

Dziś sytuacja jest inna, ale zapytaj dowolnego programistę asemblera lub osoby, które budują backendy kompilatora dla x86. Szalenie niska liczba rejestrów ogólnego przeznaczenia to nic innego jak straszny zabójca wydajności.

Turing Complete
źródło
Jedynymi poważnymi problemami związanymi z segmentową architekturą 8086 był fakt, że był tylko jeden niewydzielony rejestr segmentowy (ES), a języki programowania nie były zaprojektowane do efektywnej pracy z nim. Styl skalowanego adresowania, którego używa, działałby bardzo dobrze w języku zorientowanym obiektowo, który nie oczekuje, że obiekty będą mogły rozpoczynać się od dowolnych adresów (jeśli wyrównasz obiekty na granicach akapitu, odniesienia do obiektów będą musiały mieć tylko dwa bajty, a nie cztery). Jeśli porównać wczesny kod Macintosha z kodem na PC, 8086 wygląda całkiem nieźle w porównaniu do 68000.
supercat,
@supercat: w rzeczywistości rejestr es BYŁ poświęcony czemuś, a mianowicie tym instrukcjom łańcuchowym, które wymagały przechowywania (movs, stos) lub skanowania (cmps i scas). Biorąc pod uwagę adresowanie 64KiB z każdego rejestru segmentu, stanowiło również „brakujące łącze” do pamięci innej niż kod, dane i pamięć stosu (cs, ds, ss). Rejestry segmentowe zapewniały rodzaj schematu ochrony pamięci, w którym nie można było adresować poza blokami pamięci 64 kib rejestrów. Jakie lepsze rozwiązanie proponujesz, biorąc pod uwagę, że x86 był architekturą 16-bitową i ograniczeniami litografii?
Olof Forshell
@OlofForshell: ES był używany do instrukcji łańcuchowych, ale może być używany jako niezatwierdzony rejestr dla kodu, który ich nie używa. Sposobem na złagodzenie wąskiego gardła seg-reg bez wymagania zbyt dużej przestrzeni kodu operacji byłoby posiadanie przedrostka „rseg”, który określałby, że dla następnej instrukcji w formacie r / m pole „r” wybierze z CS / SS / DS / ES / FS / GS / ?? / ?? zamiast AX / BX / CX / DX / SI / DI / SP / BP i mieć prefiksy dla FS / GS i instrukcje dla LFS i LGS (jak LDS i LES). Nie wiem, jak wyglądała mikroarchitektura dla 8086, ale myślę, że coś takiego mogłoby zadziałać.
supercat
@supercat: jak napisałem, "rejestry również dostarczają brakującego łącza do pamięci innej niż ..." Fs i gs nie dotarły do ​​386, jak pamiętam.
Olof Forshell,
1
@OlofForshell: Nie zrobili tego, co pod wieloma względami sprawiło, że architektura 80286 była jeszcze gorsza niż architektura 8086. Chodziło mi o to, że dodanie kilku więcej rejestrów segmentowych (lub nawet jednego, jeśli o to chodzi) uczyniłoby architekturę 8086 o wiele bardziej użyteczną, a zestaw instrukcji mógłby być czystszy i bardziej użyteczny, gdyby dostęp do rejestrów segmentowych był podobny do inni.
supercat