Плохо ли повторное использование переменных для параллелизма на уровне команд и выполнения OoO? - PullRequest
0 голосов
/ 25 апреля 2018

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

Насколько я понял, процессоры могут сделать это путем анализа зависимостей данных , чтобы определить, какие инструкции могут быть выполнены первыми / в одном и том жеILP-paralell-step (выпуск).

@ edit

Попробую привести пример.Представьте себе эти два фрагмента кода:

int myResult;

myResult = myFunc1(); // 1
myResult = myFunc2(); // 2
j = myResult + 3;     // 3

-

int myFirstResult, mySecondResult;

myFirstResult = myFunc1();  // 1
mySecondResult = myFunc2(); // 2
j = mySecondResult + 3;     // 3

Они оба делают одно и то же, разница в том, что на первом я повторно использую свою переменную, а на втором я надеваю't.

Я предполагаю (и, пожалуйста, исправьте меня, если я ошибаюсь), что процессор мог выполнить инструкции 2 и 3 перед инструкцией 1 во втором примере, потому что данные будут храниться в двух разных местах (registers?).

То же самое было бы невозможно для первого примера, потому что, если он выполняет инструкцию 2 и 3 перед инструкцией 1, значение, назначенное для инструкции 1, будет сохранено в памяти (вместо значения изинструкция 2).

Вопрос :

Существует ли какая-либо стратегия для выполнения инструкций 2 и 3 перед 1, если я повторно использую переменную (как в первом примере)?

Или повторное использование переменных предотвращает параллелизм на уровне команд и выполнение OoO?

1 Ответ

0 голосов
/ 25 апреля 2018

Современный микропроцессор является чрезвычайно сложным оборудованием и уже обладает достаточной сложностью, так что понимание каждого аспекта его функционирования недоступно большинству людей.Существует дополнительный уровень, представленный вашим компилятором или средой выполнения, который увеличивает сложность.Здесь действительно можно говорить только в общих чертах, поскольку процессор ARM X может справиться с этим, чем процессор ARM Y, и оба они отличаются от Intel U или AMD V.

Если присмотреться к вашему коду:

int myResult;

myResult = myFunc1(); // 1
myResult = myFunc2(); // 2
j = myResult + 3;     // 3

Строка int myResult не обязательно делает что-нибудь процессорное.Он просто указывает компилятору, что будет переменной с именем myResult типа int.Он не инициализирован, поэтому пока ничего не нужно делать.

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

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

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

int a = 1;
int b = f();
int c = a * 2;
int d = a + 2;

int e = g(b);

Назначение a является простым и немедленным.b - это вычисленное значение.Интересно, что c и d имеют одинаковую зависимость и могут фактически выполняться параллельно.Они также не зависят от b, поэтому теоретически они могут выполняться до, во время или после вызова f(), если конечное состояние правильное.

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

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

Короткий ответ: переменные не имеют значения.Все зависит от зависимостей, вашего компилятора и возможностей вашего процессора.

...