Подтвердить что ты не робот

Что означает векторизация?

Можно ли векторизовать код? Каковы хорошие практики в отношении того, когда это делать? Что происходит внизу?

4b9b3361

Ответ 1

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

for(i=0; i<N; i++){
  a[i] = a[i] + b[i];
}

Он будет векторизован как (с использованием векторной нотации)

for (i=0; i<(N-N%VF); i+=VF){
  a[i:i+VF] = a[i:i+VF] + b[i:i+VF];
}

В основном компилятор выбирает одну операцию, которая может быть выполнена на VF-элементах массива одновременно, и делает это N/VF раз, а не выполняет одну операцию N раз.

Это повышает производительность, но повышает требования к архитектуре.

Ответ 2

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

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

http://en.wikipedia.org/wiki/Data_dependency

Некоторые компиляторы, такие как компиляторы Intel С++/Fortran, могут использовать autovectorizing code. В случае, если он не смог векторизовать цикл, компилятор Intel способен сообщить, почему он не мог этого сделать. Отчеты могут быть использованы для модификации кода таким образом, что он становится векторизованным (если это возможно)

Зависимости подробно описаны в книге "Оптимизация компиляторов для современных архитектур: подход на основе зависимости"

Ответ 3

Это генерация кода SSE.

У вас есть цикл с кодом матрицы с плавающей запятой в нем matrix1 [i] [j] + matrix2 [i] [j], а компилятор генерирует код SSE.

Ответ 4

Векторизация не должна ограничиваться одним регистром, который может содержать большие данные. Подобно использованию битового регистра "128" для хранения данных "4 x 32". Это зависит от архитектурных ограничений. В некоторой архитектуре есть разные исполнительные блоки, у которых есть собственные реестры. В этом случае часть данных может быть подана на этот исполнительный блок, и результат может быть получен из регистра, соответствующего этому исполнительному блоку.

Например, рассмотрим пример ниже.

для (i = 0; я < N; я ++)
{
a [i] = a [i] + b [i];
}



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

для (i = 0; я < (N/2); я + = 2)
{ a [i] = a [i] + b [i],


a [i + 1] = a [i + 1] + b [i + 1];
}

ПРИМЕЧАНИЕ. 2 внутри оператора for выводится из размера вектора.

Поскольку у меня есть два исполнительных блока, два оператора внутри цикла будут передаваться в два исполнительных блока. Сумма будет накапливаться в исполнительных единицах отдельно. Наконец, будет выполнена сумма накопленных значений (из двух исполнительных блоков).

Хорошая практика - это 1. Ограничения, такие как зависимость (между разными итерациями цикла), должны быть проверены до векторизации цикла.
2. Необходимо запретить вызовы функций.
3. Доступ указателя может создавать сглаживание, и его необходимо предотвратить.