W jaki sposób wiersze kodu są wykonywane przez procesor?

11

Staram się naprawdę zrozumieć, jak dokładnie język wysokiego poziomu jest konwertowany na kod maszynowy, a następnie wykonywany przez procesor.

Rozumiem, że kod jest wkompilowany w kod maszynowy, który jest kodem niskiego poziomu, którego może używać procesor. Jeśli mam oświadczenie o przypisaniu, powiedz:

x = x + 5;
y = x - 3;

Czy procesor wykonuje każdą linię pojedynczo? Więc najpierw wykona x = x + 5; instrukcja, a następnie następną instrukcją, którą wykona CPU, jest y = x- 3; Naprawdę staram się zrozumieć proces wykonywania i sposób, w jaki kod, który piszę, jest faktycznie wykonywany przez procesor.

Frankie
źródło
Możesz spróbować zrozumieć projekt jednego z procesorów open source, istnieją naprawdę proste implementacje oparte na stosie, takie jak excamera.com/sphinx/fpga-j1.html - są znacznie prostsze niż architektury 3-adresowe jak w twoim przykładzie.
SK-logic
3
Gdy zająłem się tym biznesem, miałoby to proste i dobrze zdefiniowane odpowiedzi. Obecnie procesory są niezwykle skomplikowane i robią różne rzeczy, aby zwiększyć moc przetwarzania.
David Thornley,

Odpowiedzi:

12

Wiersze kodu nie mają nic wspólnego z tym, jak procesor go wykonuje. Poleciłbym przeczytać o asemblerze, ponieważ to nauczy cię wiele o tym, jak sprzęt faktycznie robi rzeczy. Możesz również uzyskać dane wyjściowe asemblera z wielu kompilatorów.

Ten kod może się skompilować w coś takiego (w gotowym języku asemblera):

load R1, [x] ; meaning load the data stored at memory location x into register 1
add R1, 5
store [x], R1 ; store the modified value into the memory location x
sub R1, 3
store R1, [y]

Jeśli jednak kompilator wie, że zmienna nie jest ponownie używana, operacja przechowywania może nie zostać wysłana.

Aby debugger wiedział, jaki kod maszynowy odpowiada linii źródła programu, kompilator dodaje adnotacje, aby pokazać, który wiersz odpowiada miejscu w kodzie maszynowym.

maxpolun
źródło
Dlaczego nie? Architektura 3-adresowa będzie zawierała instrukcje takie jak ADD Rx, Rx, $5i SUB Ry, Rx, $3(zakładając, że zmienne xiy zostały zamapowane w rejestrach). Opisujesz podejście RISC do ładowania / przechowywania.
SK-logic
1
@ SK-logic: Chociaż może się tak zdarzyć w przypadku bardzo prostych wierszy kodu w bardzo prostych językach programowania z typami danych i operacjami, które procesor obsługuje wystarczająco dobrze, nie jest to jednak ogólny przypadek. Jest to wygodne dla ekspertów, ale najpierw ważne jest, aby zdać sobie sprawę z tego, że instrukcje kodu maszynowego są zasadniczo mało podobne do linii kodu w języku wysokiego poziomu.
@ SK-Logic: działa tylko w tym konkretnym przykładzie. Ogólnie jednak maxpolun ma rację. Instrukcje języka wysokiego poziomu muszą zostać przetłumaczone na język niższego poziomu, przy czym potrzeba więcej „biurokracji”, aby robić proste koncepcyjnie rzeczy. Myślę, że OP poprosił o przykład tej transformacji.
Andres F.
1
@ SK-Logic: OP rozpoczął swoje pytanie od „Staram się naprawdę zrozumieć, jak dokładnie język wysokiego poziomu [...]”
Andres F.,
1
@ SK-logic Kontekst brzmi „Jeśli mam instrukcję przypisania, powiedz: [fragment kodu] Czy CPU wykonuje każdą linię pojedynczo?” - wydaje mi się, że ma to być kod źródłowy w języku innym niż asembler. Mówiąc bardziej ogólnie, nie widzę żadnego wskaźnika zrozumienia, jak niski jest kod maszynowy, a niektóre sformułowania (takie jak mówienie o wierszach) wskazują na pewne nieporozumienia. Nie jest to tak niemożliwe, jak sugerujesz, nie wszyscy mieli przyjemność rzucania głową w pierwszej kolejności na niektórych prostych mikrokontrolerach (takich jak ja i najwyraźniej inni). Może Frankie powinien to wyjaśnić.
2

To zależy.

Tak, na początku naprawdę prostych maszyn, tak, kod wykonywany był po jednym wierszu na raz. Gdy maszyny stały się większe, szybsze i bardziej złożone, zaczęłaś widzieć zdolność wykonywania wielu instrukcji jednocześnie, a pamięć odczytuje i zapisuje znacznie dłużej niż operacje na rejestrach.

Optymalizacja kompilatorów musiała wziąć to pod uwagę, a podane linie można było wykonywać mniej więcej równolegle, przy czym jedna część procesora działała na obliczeniach y, podczas gdy inna część przechowywała wcześniej obliczoną nową wartość x (a obliczenie y wykorzystywało tę nową wartość z rejestru).

Control Data 6600 był pierwszą znaną mi maszyną, która zrobiła takie rzeczy. Dodanie liczby całkowitej zajęło 300 nsek. Odniesienie do pamięci (odczyt lub zapis) zajęło 1000 nsek. Mnożenie i dzielenie trwało dużo dłużej. Można wykonać około dziesięciu instrukcji równolegle, w zależności od wymaganych jednostek funkcjonalnych. Kompilatory CDC 6600 FORTRAN były BARDZO dobre w planowaniu tego wszystkiego.

John R. Strohm
źródło
W takim przypadku wprowadzenie następnej instrukcji zależy od wyniku pierwszej instrukcji, dlatego należy ją wykonać sekwencyjnie.
SK-logic
@ SK-logic: Niezupełnie. Wejście drugiego wiersza zależy od wyniku pierwszego wiersza po prawej stronie, ale w oparciu o to, co widzimy w oryginalnym przykładowym kodzie, może NIE zależeć od przechowywania do pamięci wyniku wyniku pierwsza linia. Gdyby x został uznany za lotny (w C / C ++), wówczas kompilator musiałby najpierw zapisać wynik, A NASTĘPNIE PRZEKAZYWAĆ Z PAMIĘCI, zanim zacznie obliczać nową wartość y, ponieważ „niestabilny” oznacza, że ​​coś (powiedzmy, że program obsługi przerwań) może wejść i przełożyć x między dwiema liniami.
John R. Strohm,
Założyłem, że xiy są rejestrami (a kod jest w 3-adresowym języku pseudo-zestawu, a nie w rodzaju C). W takim przypadku obie instrukcje są nieuchronnie sekwencyjne. W przeciwnym razie OP musiałby zadać dwa lub więcej różnych pytań zamiast tego.
SK-logic
Zastanawiam się, czy procesory próbowałyby „spekulować”, jaka jest wartość x? W ten sposób już wykonał kod i zapisał go w pamięci podręcznej.
Kolob Canyon
Nawet jeśli są to rejestry, W ZALEŻNOŚCI OD MASZYNY, nie można zakładać, że instrukcje wykonują się całkowicie sekwencyjnie. 6600 miał logikę planowania („tablicę wyników”), która wymuszałaby semantykę sekwencyjną, w oparciu o założenie, że programista chciał zrobić to, co oczywiste. Późniejsze maszyny pominęły ten sprzęt, zamiast tego polegały na kompilatorach, które starannie planowały instrukcje. Programiści zajmujący się programowaniem w asemblerze tych zwierząt byli WŁAŚCICIELI.
John R. Strohm,
1

Nie, nie ma odwzorowania jeden na jeden między wierszami kodu / instrukcjami w językach wyższego i niższego poziomu. W rzeczywistości obie powyższe linie są tłumaczone na wiele instrukcji kodu maszynowego , takich jak

  1. załaduj wartość z określonego adresu pamięci do rejestru
  2. zmodyfikuj wartość
  3. zapisz to z powrotem do pamięci

Rzeczywiste szczegóły tych instrukcji różnią się w zależności od platformy.

To jest podstawowy pogląd na rzeczy. Jednak w celu dalszego skomplikowania problemów nowoczesne procesory stosują między innymi takie techniki, jak potoki wykonywania , wykonywanie poza kolejnością i wiele rdzeni . Powoduje to, że CPU wykonuje wiele czynności jednocześnie, np. Rurociągi przetwarzają różne fazy kolejnych instrukcji równolegle w tej samej jednostce przetwarzania, podczas gdy wiele rdzeni może przetwarzać równolegle niezależne instrukcje.

Péter Török
źródło
0

Powinieneś przyjrzeć się szczegółom w książce, aby znaleźć więcej szczegółów na temat tego, jak to działa, być może także klasę kompilatora.

Zasadniczo twoje pytanie koncentruje się na 2 różnych aspektach.

1) W jaki sposób kod jest tłumaczony na kod maszynowy?

2) Kiedy / jak oblicza się kod przy użyciu równoległości?

Odpowiedź na 1) zależy od języka, którego używasz (chociaż w twoim przykładzie jest trywialny, więc wynik będzie taki sam). Sposób, w jaki kompilator dokonuje tłumaczenia na kod maszynowy, jest jedną z sił tego języka. Poza tym w twoim przykładzie należy wziąć pod uwagę kilka kwestii, kod powinien załadować dane do pamięci, przechowywać itp.

Wreszcie, równoległość jest funkcją, którą można wymusić z punktu widzenia programowania, ale w skrócie, niektóre procesory mogą próbować myśleć, że pewna część kodu może być uruchomiona w tym samym czasie, ponieważ są one niezależne. W twoim przypadku najwyraźniej tak nie jest, ponieważ musisz wykonywać instrukcje sekwencyjnie, więc nie, nie będzie działać w tym samym czasie.

SRKX
źródło