Так, может Вы парни говорить мне, почему разработчики ДОЛЖНЫ использовать управление исходным кодом?
Управление исходным кодом похоже на страховку! Вы надеетесь, что никогда не нуждаетесь в нем, но рады, что у Вас есть он, когда Вы делаете!
Векторизация означает, что компилятор обнаруживает, что ваши независимые инструкции могут выполняться как одна инструкция 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 раз.
Это увеличивает производительность, но предъявляет больше требований к архитектуре.
Это генерация кода SSE.
У вас есть цикл с матрицей с плавающей запятой код в нем matrix1 [i] [j] + matrix2 [i] [j] и компилятор генерирует код SSE.
Может быть, также взгляните на libSIMDx86 (исходный код).
Хороший пример, хорошо объясненный:
Как упоминалось выше, векторизация используется для использования инструкций SIMD, которые могут выполнять идентичные операции с разными данными, упакованными в большие регистры.
Общее руководство, позволяющее компилятору автовекторизовать цикл должен гарантировать отсутствие потоковых и антизависимых элементов данных ч / б в различных итерациях цикла.
http://en.wikipedia.org/wiki/Data_dependency
Некоторые компиляторы, такие как Intel Компиляторы C ++ / Fortran могут выполнять автовекторизацию кода. Если не удалось векторизовать цикл, компилятор Intel может сообщить, почему он не смог этого сделать. Отчеты можно использовать для изменения кода, чтобы он стал векторизуемым (если это возможно)
. Зависимости подробно рассматриваются в книге «Оптимизация компиляторов для современных архитектур:
Векторизация не должна ограничиваться одним регистром, который может хранить большие данные. Как использование 128-битного регистра для хранения данных размером 4 x 32 бита. Это зависит от архитектурных ограничений. Некоторые архитектуры имеют разные исполнительные блоки, у которых есть собственные регистры. В этом случае часть данных может быть передана в этот исполнительный блок, а результат может быть взят из регистра, соответствующего этому исполнительному блоку.
Например, рассмотрим следующий случай.
для (i = 0 ; i
{
a [i] = a [i] + b [i];
}
Если я работаю над архитектурой с двумя исполнительными модулями, то размер моего вектора определяется как два. Упомянутый выше цикл будет преобразован в
for (i = 0; i <(N / 2); i + = 2)
{
a [i] = a [i] + b [i];
a [i + 1] = a [i + 1] + b [i + 1];
}ПРИМЕЧАНИЕ: 2 внутри оператора for определяется размером вектора.
Поскольку у меня есть два исполнительных модуля, два оператора внутри цикла будут переданы в два исполнительных модуля. Сумма будет накапливаться в исполнительных единицах отдельно. Наконец, будет проведена сумма накопленных значений (от двух исполнительных блоков).
Передовой практикой является
1. Перед векторизацией цикла необходимо проверить такие ограничения, как зависимость (между различными итерациями цикла).
2. Необходимо предотвратить вызовы функций.
3. Доступ с указателем может создать псевдоним, и его необходимо предотвратить.