Ostatnio czytałem archiwa SO i napotkałem stwierdzenia przeciwko architekturze x86.
Dlaczego potrzebujemy innej architektury procesora dla serwera, mini / mainframe i mieszanego rdzenia? mówi
„ Architektura komputerów PC to bałagan, każdy programista systemów operacyjnych powiedziałby ci to ”.Czy nauka języka asemblera jest warta wysiłku?( zarchiwizowane ) mówi
„ Zrozum, że architektura x86 jest w najlepszym razie okropna ”Czy jest jakiś łatwy sposób na nauczenie się asemblera x86? mówi:
„ Większość uczelni uczy asemblera na czymś takim jak MIPS, ponieważ jest to dużo prostsze do zrozumienia, asemblacja x86 jest naprawdę brzydka ”
i wiele innych komentarzy, takich jak
„W porównaniu z większością architektur, X86 jest do niczego”.
„ Zdecydowanie powszechna jest opinia, że X86 jest gorszy od MIPS, SPARC i PowerPC ”
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.
Odpowiedzi:
Kilka możliwych powodów:
IN
iOUT
)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
MOVSB
instrukcja wymaga stosunkowo dużego bloku kodu C, aby opisać, co robi: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.
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ą.
źródło
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.
źródło
add
następnyadd
. 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.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.
źródło
Mam tutaj kilka dodatkowych aspektów:
Rozważmy operację „a = b / c” x86 zaimplementowałaby to jako
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:
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”.
źródło
there typically won't be a reminder
ale wiki mówi, że mips ma to: en.wikipedia.org/wiki/MIPS_instruction_set#IntegerMyś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:
mov %eax, 0x1c(%esp,%edi,4)
np. Tryby adresowania i nie są one podzielone.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ódło
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.
źródło
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.
źródło
Oprócz powodów, o których ludzie już wspomnieli:
__cdecl
,__stdcall
,__fastcall
, itd.źródło
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.
źródło
x86 ma bardzo, bardzo ograniczony zestaw rejestrów ogólnego przeznaczenia
promuje bardzo nieefektywny styl rozwoju na najniższym poziomie (piekło CISC) zamiast efektywnej metodologii ładowania / przechowywania
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ą
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.
źródło