Что означает векторизация? - PullRequest
26 голосов
/ 04 октября 2009

Это хорошая идея векторизовать код? Каковы хорошие практики с точки зрения того, когда это делать? Что происходит под ней?

Ответы [ 5 ]

41 голосов
/ 04 октября 2009

Векторизация означает, что компилятор обнаруживает, что ваши независимые инструкции могут быть выполнены как одна 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 раз.

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

12 голосов
/ 10 октября 2009

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

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

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

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

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

3 голосов
/ 04 ноября 2009

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

Например, рассмотрим приведенный ниже случай.

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



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

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


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

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

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

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

3 голосов
/ 04 октября 2009

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

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

0 голосов
/ 05 октября 2009

Может также взглянуть на libSIMDx86 (исходный код).

Хороший хорошо объясненный пример:

Избегание ветвей: пример Altivec

...