Как строки кода выполняются процессором?

11

Я пытаюсь понять, как именно язык высокого уровня преобразуется в машинный код, а затем исполняется процессором.

Я понимаю, что код скомпилирован в машинный код, который является кодом низкого уровня, который может использовать процессор. Если у меня есть заявление о назначении, скажите:

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

Процессор выполняет каждую строку по одной за раз? Так что сначала он выполнит x = x + 5; инструкция, а затем следующая инструкция, которую ЦП выполнит, это y = x- 3; Я действительно пытаюсь понять процесс выполнения и как на самом деле код, который я пишу, исполняется процессором.

Фрэнки
источник
Возможно, вы захотите попытаться понять конструкцию одного из процессоров с открытым исходным кодом, есть несколько действительно простых реализаций на основе стека, таких как excamera.com/sphinx/fpga-j1.html - они намного проще, чем трехадресные архитектуры как в вашем примере.
SK-logic
3
Когда я занялся этим бизнесом, у него были бы простые и четкие ответы. В настоящее время процессоры чрезвычайно сложны и делают все возможное для увеличения вычислительной мощности.
Дэвид Торнли

Ответы:

12

Строки кода не имеют ничего общего с тем, как процессор их выполняет. Я бы порекомендовал почитать на ассемблере, потому что это многому научит вас, как на самом деле работает аппаратное обеспечение. Вы также можете получить вывод ассемблера от многих компиляторов.

Этот код может скомпилироваться во что-то вроде (на языке ассемблера):

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]

Однако, если компилятор знает, что переменная больше не используется, операция сохранения может не сработать.

Теперь, чтобы отладчик знал, какой машинный код соответствует строке исходного кода программы, компилятор добавляет аннотации, чтобы показать, какая строка соответствует месту в машинном коде.

maxpolun
источник
Почему бы и нет? 3-адресная архитектура будет иметь такие инструкции, как ADD Rx, Rx, $5и SUB Ry, Rx, $3(при условии, что переменные x и y были отображены в регистры). Вы описываете подход RISC загрузки / хранения.
SK-logic
1
@ SK-logic: Хотя это может произойти для очень простых строк кода на очень простых языках программирования с типами данных и операциями, которые ЦП поддерживает достаточно хорошо, это ни к чему не относится. Это удобно для экспертов, но сначала важно понять, что инструкции машинного кода обычно мало похожи на строки кода в языке высокого уровня.
@ SK-Logic: это работает только для этого конкретного примера. В целом, однако, maxpolun прав. Операторы языка высокого уровня должны быть переведены на язык более низкого уровня, с большим количеством «бюрократизма», необходимого для выполнения концептуально простых вещей. Я полагаю, что ОП спрашивал пример этого преобразования.
Андрес Ф.
1
@ SK-Logic: ОП начал свой вопрос с «Я пытаюсь действительно понять, как именно язык высокого уровня [...]»
Андрес Ф.
1
@ SK-logic Контекст: «Если у меня есть оператор присваивания, скажем: [фрагмент кода] ЦП выполняет каждую строку по одной за раз?» - мне кажется, что он предназначен для исходного кода на языке, не являющемся ассемблером. В более общем плане я не вижу индикатора понимания того, насколько низкоуровневым является машинный код, и некоторые фразы (например, речь о строках) указывают на некоторые неправильные представления. Это не так невозможно, как вы предполагаете, не у всех было удовольствие от того, что сначала бросали голову некоторым простым микроконтроллерам (таким как я и, очевидно, другие). Возможно, Фрэнки должен уточнить.
2

Это зависит.

В первые дни действительно простых машин, да, код выполнялся по одной строке за раз. По мере того, как машины становились больше, быстрее и сложнее, вы начали видеть способность выполнять несколько инструкций одновременно, а чтение и запись в память занимали намного больше времени, чем операции с регистрами.

Оптимизирующие компиляторы должны были принять это во внимание, и заданные вами строки могли бы выполняться «более или менее» параллельно, при этом одна часть процессора работала над вычислением y, тогда как другая часть сохраняла ранее вычисленное новое значение x (и вычисление y использовало это новое значение из регистра).

Control Data 6600 была первой из известных мне машин, которая делала подобные вещи. Целочисленное сложение заняло 300 нсек, обращение к памяти (чтение или запись) заняло 1000 нсек, умножение и деление заняли много дольше. До десяти инструкций могут выполняться параллельно в зависимости от того, какие функциональные блоки требуются. Компиляторы CDC 6600 FORTRAN были ОЧЕНЬ хороши в планировании всего этого.

Джон Р. Штром
источник
В этом случае ввод следующей инструкции зависит от результата первой инструкции, поэтому он должен выполняться последовательно.
SK-logic
@ SK-логика: не совсем. Ввод второй строки зависит от результата с правой стороны первой строки, но, основываясь исключительно на том, что мы видим в исходном примере кода, он НЕ может зависеть от сохранения в памяти результата результата первая строка. Если бы x было объявлено как volatile (в C / C ++), то компилятору потребуется сначала сохранить результат, а затем перезагружать его из памяти, прежде чем начинать вычислять новое значение y, поскольку «volatile» означает, что (скажем, обработчик прерываний) может войти и убить x между двумя строками.
Джон Р. Штром
Я предположил, что x и y являются регистрами (и код написан на языке псевдосборки с 3 адресами, а не на языке C). В этом случае обе инструкции неизбежно являются последовательными. В противном случае ОП должен был задать два или более разных вопроса вместо этого.
SK-logic
Интересно, будут ли процессоры «спекулировать», какова стоимость x? Таким образом, он уже выполнил код и сохранил его в кеше.
Каньон Колоб
Даже если они являются регистрами, в зависимости от машины, вы не можете предполагать, что инструкции выполняются полностью последовательно. У 6600 была логика планирования («табло»), которая вынуждала последовательную семантику, основываясь на предположении, что программист хотел сделать очевидное. Более поздние машины опускали это оборудование, вместо этого полагаясь на компиляторы, чтобы тщательно планировать инструкции. Программисты-люди, занимающиеся программированием на ассемблере на этих животных, были ОНИ СОБСТВЕННЫ.
Джон Р. Штром
1

Нет, не существует однозначного сопоставления между строками кода / инструкциями на языках более высокого и более низкого уровня. На самом деле обе строки выше переведены в несколько команд машинного кода , как

  1. загрузить значение из определенного адреса памяти в регистр
  2. изменить значение
  3. запишите это обратно в память

Фактические детали этих инструкций различаются в зависимости от платформы.

Это основной взгляд на вещи. Однако для дальнейших вопросов затруднят, современные процессоры применяются методы как конвейеры выполнения , внеочередного исполнения и нескольких ядер , среди других. Это приводит к тому, что процессор выполняет несколько операций одновременно, например, конвейеры обрабатывают различные фазы последующих команд параллельно в одном и том же процессоре, в то время как несколько ядер могут обрабатывать независимые инструкции параллельно.

Петер Тёрёк
источник
0

Вы должны подробно изучить книгу, чтобы узнать больше о том, как она работает, возможно, также и о классе компилятора.

По сути, ваш вопрос сосредоточен на двух разных аспектах.

1) Как код переводится в машинный код?

2) Когда / как код вычисляется с использованием распараллеливания?

Ответ на 1) зависит от языка, который вы используете (хотя для вашего примера это тривиально, поэтому вывод будет таким же). То, как компилятор выполняет перевод в машинный код, является одной из сил языка. Кроме того, в вашем примере необходимо учесть несколько проблем: код должен загружать данные в память, сохранять их и т. Д.

Наконец, распараллеливание - это функция, которую вы можете использовать с точки зрения программирования, но в двух словах, некоторые процессоры могут пытаться думать, что некоторая часть кода может быть запущена одновременно, потому что они независимы. Очевидно, что в вашем случае это не так, поскольку вам нужно выполнять операторы последовательно, поэтому нет, он не будет выполняться одновременно.

SRKX
источник