390 likes | 664 Views
Оптимизирующий компилятор. Векторизация. Тенденция в развитии микропроцессоров:. Параллельное выполнение инструкций. Последовательное выполнение инструкций. Конвейеризация Суперскалярность Векторные операции Многоядерные и многопроцесорные решения
E N D
Оптимизирующий компилятор.Векторизация.
Тенденция в развитии микропроцессоров: Параллельное выполнение инструкций Последовательное выполнение инструкций • Конвейеризация • Суперскалярность • Векторные операции • Многоядерные и многопроцесорные решения Оптимизирующий компилятор – инструмент, транслирующий исходный код программы в исполняемый модуль и оптимизирующий исходный код для получения лучшей производительности.Параллелизация – трансформация последовательно выполняющейся программы в программу, в которой наборы инструкций выполняются одновременно и сохраняется «смысл» программы.
Одна из технологий параллелизации программы – векторизация циклов. C[1] = A[1]+B[1] C[2] = A[2]+B[2] C[3] = A[3]+B[3] C[4] = A[4]+B[4] Векторизация A[1] A[2] A[3] A[4] B[1] B[2] B[3] B[4] B[1] A[1] + B[2] A[2] B[3] A[3] B[4] A[4] = C[1] C[2] C[3] C[4] C[1] C[2] C[3] C[4]
Типичная векторная инструкцияпредставляет собой операцию над двумя векторами в памяти или в регистрах фиксированной длины. Данные векторные регистры могут быть загружены из памяти за одну операцию или по частям. • Векторизация - это компиляторная оптимизация, которая заменяет скалярный код на векторный. Т.е. эта оптимизация «упаковывает» данные в вектора и заменяет скалярные операции на операции с векторами (пакетами). • A(1:n:k) – секция массива в Фортране. • for(i=0;i<U,i++) { for(i=0;i<U;i+=vl) { • S1: lhs1[i] = rhs1[i]; S1: lhs1[i:i+vl-1:1] = rhs1[i:i+vl-1:1]; • … => … • Sn: lhsn[i] = rhsn[i]; Sn: lhsn[i:i+vl-1:1] = rhsn[i:i+vl+1:1]; • } }
MMX,SSEВекторные инструкции и векторизация Первоначально была предложена технология MMX. MMX (Multimedia Extensions) – набор инструкций, выполняющиххарактерныедляпроцессовкодирования/декодированияпотоковыхаудио/видеоданныхдействия. MMM0-MMM7 64 битные регистры для работы с целыми числами концепция пакетов, каждый из этих регистров мог хранить 2 – 32 битных целых числа 4 - 16 битных 8 - 8 битных 47 инструкций, которые деляться на несколько груп: перемещение данных арифметические сравнения конвертации логические распаковки сдвига пустые инструкции получения состояния
SSE(Streaming SIMD Extensions, потоковое SIMD-расширение процессора) — набор инструкций позволяющий работать с множеством данных – SIMD(Single Instruction, Multiple Data, Одна инструкция — множество данных). SSE включает в себя : 8128-битных регистров(xmm0 до xmm7) набор инструкций, который производит операции со скалярными и упакованными типами данных. Технология EMM64T добавляла к этому набору еще 8 128 битных регистра (xmm8 до xmm15). SSE2,SSE3,SSEE3,SSE4 – последующие расширения этой идеи. SSE2 добавил тип упакованных данных с плавающей точкой двойной точности. AVX новое расширение системы команд (Advanced vector extensions) AVX предоставляет различные улучшения, новые инструкции и новую схему кодирования машинных кодов. Размер векторных регистров SIMD увеличивается с 128 до 256 бит. Регистры YMM0-YMM15 Существующие 128-битные инструкции используют младшую половину YMM регистров.
Данные различных типов могут быть упакованы в векторные регистры следующим образом: Выбор подходящего для вычислений типа данных может существенно сказаться на производительности приложения.
Optimization with SwitchesSIMD – SSE, SSE2, SSE3, SSE4.2 Support 2x doubles 4x floats SSE4.2 SSE3 SSE2 1x dqword 16x bytes SSE 8x words MMX* 4x dwords 2x qwords * MMX actually used the x87 Floating Point Registers - SSE, SSE2, and SSE3 use the new SSE registers
Группы иструкций: • Инструкции перемещения данных: (data movement instructions) : • Instruction Suffix Description • movdqa перемещение выравненного двойного четверного слова (double quadword) • movdqu перемещение невыравненного двойного четверного слова • mova [ ps, pd ] перемещение выравненого вещественного • movu [ ps, pd ] перемещение невыравненного вещественного • movhl [ ps ] перемещение верхней части упакованного вещественного вниз • movlh [ ps ] перемещение нижней части упакованного вещественного вверх • movh [ ps, pd ] перемещение верхней части упакованного вещественного • movl [ ps, pd ] перемещение нижней части упакованного вещественного • mov [ d, q, ss, sd ] перемещение скалярных данных • lddqu перемещение невыравненного двойного четверного слова • mov<d/sh/sl>dup перемещение и дублирование • pextr [ w ] извлечение слова • pinstr [ w ] вставка слова • pmovmsk [ b ] перемещение маски • movmsk [ ps, pd ] перемещение маски • Инструкции для перемещения выравненных (aligned) данных не могут использоваться для работы с адресами памяти не выравнеными на 16.
Целочисленные арифметические инструкции : • Instruction Suffix Description • padd [ b, w, d, q ] векторное сложение (знаковые и беззнаковые) • psub [ b, w, d, q ] векторное вычитание (знаковые и беззнаковые) • padds [ b, w ] векторное сложениес насыщением (знаковые) • paddus [ b, w ] векторное сложениес насыщением (беззнаковые) • psubs [ b, w ] векторное вычитание с насыщением (знаковые) • psubus [ b, w ] векторное вычитание с насыщением (беззнаковые) • pmins [ w ] векторный минимум (знаковые) • pminu [ b ] векторный минимум (беззнаковый) • pmaxs [ w ] векторный максимум (знаковый) • pmaxu [ b ] векторный максимум (беззнаковый)
Арифметические операции с вещественными числами : • Instruction Suffix Description • add [ ss, ps, sd, pd ] сложение • div [ ss, ps, sd, pd ] деление • min [ ss, ps, sd, pd ] минимум • max [ ss, ps, sd, pd ] максимум • mul [ ss, ps, sd, pd ] умножение • sqrt [ ss, ps, sd, pd ] квадратный корень • sub [ ss, ps, sd, pd ] вычитание • rcp [ ss, ps] приблизительноеобратное • rsqrt [ ss, ps] приблизительный обратный квадратный корень • Идиоматические арифметические инструкции : • Instruction Suffix Description • pang [ b, w ] векторное среднее с округлением (беззнаковые) • pmulh/pmulhu/pmull [ w ] векторное умножение • psad [ bw ] векторная сумма абсолютных разниц (беззнаковые) • pmadd [ wd ] векторное умножение и сложение (знаковые) • addsub [ ps, pd ] вещественное сложение/вычитание • hadd [ ps, pd ] вещественное горизонтальное сложение • hsub [ ps, pd ] вещественное горизонтальное вычитание
Логические инструкции : • Instruction Suffix Description • pand побитовый логический AND • pandn побитовый логический AND-NOT • por побитовый логический OR • pxor побитовый логический XOR • and [ ps, pd ] побитовый логический AND • andn [ ps, pd ] побитовый логический AND-NOT • or [ ps, pd ] побитовый логический OR • xor [ ps, pd ] побитовый логический XOR • Инструкции сравнения : • Instruction Suffix Description • pcmp<cc> [ b, w, d ] векторное сравнение • cmp<cc> [ ss, ps, sd, pd ] вещественное сравнение • Здесь <cc> обозначает операцию сравнения. • lt – меньше, gt – больше, eq - равно
Конвертирующие инструкции : • Instruction Suffix Description • packss [wb, dw] упаковывать с насыщением(знаковые) • paсkus [wb] упаковывать с насыщением (беззнаковые) • cvt<s2d> конвертация • cvtt<s2d> конвертация с усечением • Инструкции сдвига : • Instruction Suffix Description • psll [ w, d, q, dq ] логический сдвиг влево • psra [w, d] арифметический сдвиг вправо • psrl [ w, d, q, dq ] правый логический сдвиг • Перестановочные (shuffle) инструкции : • Instruction Suffix Description • pshuf [ w, d ] векторная перестановка • pshufh [w] векторная перестановка верхней части • pshufl [w] векторная перестановка нижней части • ырга [ ps, pd ] перестановка
Расспаковывающие инструкции : • Instruction Suffix Description • punpckh [bw, wd, dq, qdq] распаковать верхнюю часть • punpckl [bw, wd, dq, qdq] распаковать нижнюю часть • unpckh [ps, pd] распаковать верхнюю часть • unpckl [ps, pd] распаковать нижнюю часть • Инструкции работы с кэшем : • Instruction Suffix Description • movnt [ ps, pd, q, dq ] перемещать выравненные не временные данные • prefetch<hint> подрузить в кэш • Управляющие состоянием инструкции • Обычно используются опреционной системой.
Три набора опций для использования процессорно-специфических расширений • Опции –x<EXT> например –xSSE4_1 • Проводит проверку процессора • Ошибка времени выполнения в случае запуска программы на прцессоре отличном от указанного в опции • Опции –m<EXT> например –mSSE3 • Нет проверки • Падение программы при выполнении специфической процессорной инструкции в случае запуска на процессоре не поддерживающем указанное расширение • Опции –ax<EXT> например –axSSE4_2 • Автоматический выбор оптимального варианта – наличие кода для разных векторных расширений • Проверка процессора доступна только для Интеловских процессоров • Для не Интеловского прцессора используется умолчательный код • Умолчательная опция –mSSE2
Типичная векторная инструкцияпредставляет собой операцию над двумя векторами в памяти или в регистрах фиксированной длины. Данные векторные регистры могут быть загружены из памяти за одну операцию или по частям. • Описание основ SSE технологии можно прочитать в CHAPTER 10PROGRAMMING WITHSTREAMING SIMD EXTENSIONS (SSE)документа«Intel 64 and IA-32 Intel Architecture Software Developer's Manual»(Volume 1) • Microsoft Visual Studio поддерживает набор SSE интринсиков, которые позволяют напрямую использовать SSE инструкции из С/C++ кода. Это позволяет осуществлять векторизацию вручную. Для этого необходимо подключить xmmintrin.h, который определяет тип __m128 (это векторный тип) и операции с ним. • Допустим мы хотим векторизовать вручную цикл: • for(i=0;i<N;i++) • C[i]=A[i]*B[i]; • Для этого необходимо : • организовать/заполнить векторные переменные • запустить интринсик для умножения векторных переменных • скопировать результаты вычислений в память • (Для экпериментов использовался Intel компилятор версии 12.0) (Использовать icl –help для получения опций компилятора. )
#include <stdio.h> #include <xmmintrin.h> #define N 40 int main() { float a[N][N][N],b[N][N][N],c[N][N][N]; int i,j,k,rep; __m128 *xa,*xb,*xc; for(i=0;i<N;i++) for(j=0;j<N;j++) for(k=0;k<N;k++) { a[i][j][k]=1.0; b[i][j][k]=2.0;} for(rep=0;rep<10000;rep++) { #ifdef PERF for(i=0;i<N;i++) for(j=0;j<N;j++) for(k=0;k<N;k+=4) { xa=(__m128*)&(a[i][j][k]); xb=(__m128*)&(b[i][j][k]); xc=(__m128*)&(c[i][j][k]); *xc=_mm_mul_ps(*xa,*xb); } #else for(i=0;i<N;i++) for(j=0;j<N;j++) for(k=0;k<N;k++) c[i][j][k]=a[i][j][k]*b[i][j][k]; #endif } printf("%f\n ",c[21][11][18]); } Пример, иллюстрирующий векторизацию с помощью SSE интринсиков. icl -Od test.c -Fetest1.exe icl -Od test.c -DPERF -Fetest_opt.exe time test1.exe 2.000000 CPU time for command: 'test1.exe' real 3.406 sec user 3.391 sec system 0.000 sec time test_opt.exe 2.000000 CPU time for command: 'test_opt.exe' real 1.281 sec user 1.250 sec system 0.000 sec
Полученный прирост производительности - 2.7x. • Для работы некоторых векторных инструкций необходимо, чтобы адреса в памяти были выравнены на 16. В нашем тесте это получилось случайно, в реальном случае нужно об этом заботиться. • Тест оптимизированный компилятором показывает следующий результат: • icl test.c -Qvec_report3 -Fetest_intel_opt.exe • time test_intel_opt.exe • 2.000000 • CPU time for command: 'test_intel_opt.exe' • real 0.328 sec • user 0.313 sec • system 0.000 sec
Допустимость векторизации • Векторизация – перестановочная оптимизация. Операции меняют порядок выполнения. • Перестановочные оптимизации допустимы, если не изменяется порядок зависимостей. • Таким образом мы получили критерий допустимости векторизации в терминах зависимостей. • Простейший вариант – зависимостей в векторизуемом цикле нет. • Зависимости в векторизуемом цикле есть, но их порядок после векторизации совпадает с порядком в невекторизованном цикле.
/Qvec-report[n] • control amount of vectorizer diagnostic information • n=0 no diagnostic information • n=1 indicate vectorized loops (DEFAULT) • n=2 indicate vectorized/non-vectorized loops • n=3 indicate vectorized/non-vectorized loops and prohibiting • data dependence information • n=4 indicate non-vectorized loops • n=5 indicate non-vectorized loops and prohibiting data • dependence information • Использование: icl -c -Qvec_report3 loop.c • Примеры диагностики: • C:\loops\loop1.c(5) (col. 1): remark: LOOP WAS VECTORIZED. • C:\loops\loop3.c(5) (col. 1): remark: loop was not vectorized: vectorization possible but seems inefficient. • C:\loops\loop6.c(5) (col. 1): remark: loop was not vectorized: nonstandard loop is not a vectorization candidate.
Фортран акктивно используется в описании векторизации, поскольку имеет удобное понятие секции массива. • В упрощенном виде: • DO I=1,N • A(I)=… • END DO • при векторизации переводится в • DO I=1,N/K • A(I:I+K)=… • END DO • где K – число элементов матрицы A размещаемых в векторном регистре. • Наглядным критерием возможности векторизации является то факт, что введение секций массива не порождает зависимостей. • DO I=1,N DO I=1,N/K • A(I)=A(I)+C => A(I:I+K) = A(I:I+K)+C • END DO END DO • Может быть векторизован. • DO I=1,N DO I=1,N/K • A(I+1)=A(I)+C => A(I+1:I+1+K)=A(I:I+K)+C • END DO END DO • Не может быть векторизован.
Предположение: Цикл можно векторизовать, если дистанция для зависимости >= K, где K – количество элементов массива, входящих в векторный регистр. Проверяем утверждение с помощью компилятора: ifort test.F90 -o a.out –vec_report3 echo ------------------------------------- ifort test.F90 -DPERF -o b.out –vec_report3 ./build.sh test.F90(11): (col. 1) remark: loop was not vectorized: existence of vector dependence. ------------------------------------- test.F90(11): (col. 1) remark: LOOP WAS VECTORIZED. PROGRAM TEST_VEC INTEGER,PARAMETER :: N=1000 #ifdef PERF INTEGER,PARAMETER :: P=4 #else INTEGER,PARAMETER :: P=3 #endif INTEGER A(N) DO I=1,N-P A(I+P)=A(I) END DO PRINT *,A(50) END
Факторы, влияющие на производительность векторизованного кода. Рассмотрим такой тест: INTEGER :: A(1000),B(1000) INTEGER I,K INTEGER, PARAMETER :: REP = 500000 A = 2 DO K=1,REP CALL ADD(A,B) END DO PRINT *,SHIFT,B(101) CONTAINS SUBROUTINE ADD(A,B) INTEGER A(1000),B(1000) INTEGER I !DEC$ UNROLL(0) DO I=1,1000-SHIFT B(I) = A(I+SHIFT)+1 END DO END SUBROUTINE END
ifort test1.F90 -O2 -Ob0 /fpp /DSHIFT=0 -Fea.exe -Qvec_report >a.out 2>&1 ifort test1.F90 -O2 -Ob0 /fpp /DSHIFT=1 -Feb.exe -Qvec_report >b.out 2>&1 %TIME% a.exe %TIME% b.exe Скомпилируем исходный тест, передав в программу разные значения переменной SHIFT. Получим: C:\users\aanufrie\students\continuous2>C:\u4win\bin\x86_win32\time.exe a.exe 0 3 CPU time for command: 'a.exe' real 0.125 sec user 0.094 sec system 0.000 sec C:\users\aanufrie\students\continuous2>C:\u4win\bin\x86_win32\time.exe b.exe 1 3 CPU time for command: 'b.exe' real 0.297 sec user 0.281 sec system 0.000 sec
ifort test1.F90 -O2 -Ob0 /fpp /DSHIFT=0 /Fas -Ob0 -Qvec_report3 -S –Fatest0.s ifort test1.F90 -O2 -Ob0 /fpp /DSHIFT=1 /Fas -Ob0 -S -Qvec-report3 –Fatest1.s Для данного цикла имеем для быстрого случая: .B2.5: ; Preds .B2.5 .B2.4 $LN83: ;;; B(I) = A(I+SHIFT)+1 movdqa xmm1, XMMWORD PTR [eax+ecx*4] ;17.11 $LN84: paddd xmm1, xmm0 ;17.4 $LN85: movdqa XMMWORD PTR [edx+ecx*4], xmm1 ;17.4 $LN86: add ecx, 4 ;16.3 $LN87: cmp ecx, 1000 ;16.3 $LN88: jb .B2.5 ; Prob 99% ;16.3
Для данного цикла имеем для медленного случая: • .B2.5: ; Preds .B2.5 .B2.4 • $LN81: • ;;; B(I) = A(I+SHIFT)+1 • movdqu xmm1, XMMWORD PTR [4+eax+ecx*4] ;17.11 • $LN82: • paddd xmm1, xmm0 ;17.4 • $LN83: • movdqa XMMWORD PTR [edx+ecx*4], xmm1 ;17.4 • $LN84: • add ecx, 4 ;16.3 • $LN85: • cmp ecx, 996 ;16.3 • $LN86: • jb .B2.5 ; Prob 99% ;16.3 • MOVDQA—Move Aligned Double Quadword • MOVDQU—Move Unaligned Double Quadword
Производительность векторизованного цикла зависит от того, каким образом векторизуемые объекты расположены в памяти. Поэтому первый аспект имеющий огромное значение для производительности программы –выравнивание данных в памяти. • Выравнивание структур данных (Data Structure Alignment) это метод с помощью которого данные располагаются и доступаются в компьютерной памяти. Это понятие заключает в себе два различных, но взаимосвязанных вопроса: выравнивание данных (Data alignment) и заполнение структур данных (Data structure padding). • Выравнивание данных определяет, как те или иные данные расположены относительно границ памяти. Этот факт как правило связан с типом данных. • Заполнение структур данных подразумевает вставку безымянных полей в структуру данных, для того чтобы сохранить относительное выравнивание полей структуры.
Информация о выравнивании может быть получена с помощью интринсика __alignof__. Размер переменной данного типа и выравнивание по умолчанию может зависеть от типа компилятора. • printf("int: sizeof=%d align=%d\n",sizeof(a),__alignof__(a)); • Выравнивание для 32 битного Интеловского C++ компилятора: • bool sizeof = 1 alignof = 1 • wchar_t sizeof = 2 alignof = 2 • short int sizeof = 2 alignof = 2 • int sizeof = 4 alignof = 4 • long int sizeof = 4 alignof = 4 • long long int sizeof = 8 alignof = 8 • float sizeof = 4 alignof = 4 • double sizeof = 8 alignof = 8 • long double sizeof = 8 alignof = 8 • void* sizeof = 4 alignof = 4 • Те же самые правила используются и при выравнивании массивов. • Существует возможность выравнять объект необходимым образом: • __declspec(align(16)) float x[N];
structfoo{ bool a; char pad1[1]; short b; char pad2[4]; long long c; bool d; char pad3[7]; }; • struct foo { • bool a; • short b; • long long c; • bool d; • }; Порядок полей в структуре оказывает влияние на размер объекта производного типа. С целью уменьшения размера объекта рекомендуется размещать поля в объекте в порядке уменьшения их размера.Можно использовать __declspec для выравнивания полей структуры. typedef struct aStuct{ __declspec(align(16)) float x[N]; __declspec(align(16)) float y[N]; __declspec(align(16)) float z[N]; };
Производительность векторизованного цикла зависит от того как обрабатываются векторные регистры, поэтому в общем случае скалярный цикл, работающий с одним массивом DO I=K,N A(I)=… END DO векторизуется следующим образом: if(! N<K) { t1=N-K+1; /* общее кол-во итераций */ t2=&A[K]&15; if(t2!=0) { /* проверяем выравнен ли объект в памяти */ t2=(16-t2)/SIZEOF(array_element); } if(t1<4+t2) { /* нечего векторизовать */ t3=0; goto TAIL; } t3=t1-((t1-t2)&3)); /* кол-во векторизованных итераций */ DO I=1,t2 /* головной цикл */ A[I+K-1]=… END DO DO I=t2+1,t3,4 /* векторизованный цикл */ A[I+K-1:I+K+3]=… END DO TAIL: DO I=t3+1,t1/* хвостовой скалярный цикл */ A[I+K-1]=… END DO }
Каким образом правильное выравнивание может помочь в случае скалярного цикла, обрабатывающего несколько массивов? void Calculate(float * restrict a,float * restrict b,float * restrict c ,int n) { int i; for(i=0;i<n;i++) { a[i] = ((a[i]*a[i])+(b[i]*b[i]))/(c[i]*c[i]); } return; } icl -S vec7.c -Qstd=c99 -QxSSE4.2 -O3 Легко найти векторизованный цикл и убедиться, что используются векторные инструкции для доступа к невыравненной памяти. .B1.20:: ; Preds .B1.18 .B1.20 movss xmm2, DWORD PTR [rcx+r9*4] ;1.6 movss xmm0, DWORD PTR [rdx+r9*4] ;1.6 ... В компиляторе есть возможность предупредить компилятор о том, что переданные в функцию аргументы будут выравнены: __assume_aligned(a,16);
Добавив в нашу функцию следующие строки __assume_aligned(a,16); __assume_aligned(b,16); __assume_aligned(c,16); мы получим код без головного выравнивающего цикла, использующий инструкции movaps для доступа к памяти. Оценим выгодность полученного кода добавив к нашей функции функцию main следующего вида: #include <stdio.h> #define N 1000 extern void Calculate(float *x,float *y,float *z,int n); int main() { __declspec(align(16)) float x[N]; __declspec(align(16)) float y[N]; __declspec(align(16)) float z[N]; int i,rep; for(i=0;i<N;i++) { x[i] = 1;y[i] = 1; z[i] = 1; } for(rep=0;rep<10000000;rep++) { Calculate(&x[0],&y[0],&z[0],N);} printf("x[0]=%f\n",x[0]); } Icl main.c vec8.c -Qstd=c99 -QxSSE4.2 -O3 -Fevec8 => 5.43s icl main.c vec7.c -Qstd=c99 -QxSSE4.2 -O3 –Fevec7 => 5.57s
Векторизация может ухудшить производительность маленьких циклов. INTEGER I,J,A(100) INTEGER,VOLATILE :: K,N K=1 N=11 DO J=1,1000000 DO I=K,N A(I)=J END DO END DO PRINT *,A(55) END Без векторизации тест выполняется 0.10s а с векторизацией 0.13s. Почему так происходит? Цикл в примере разложится на 3 части: от 2 до 5 – скалярный цикл (3 итерации) от 5 до 8 – векторизированный цикл (1 итерация) от 9 до 11 – скалярный цикл (3 итерации)
Векторизация в общем случае может быть выполнена не только на внутреннем цикле. Если мы имеем вложенное множество циклов и внутренний цикл не может быть векторизован из-за существования каких-либо зависимостей, то можно пытаться векторизовать внешний цикл: DO I=1,N DO J=1,K A(J,I) =.. END DO END DO Векторизация DO I=1,N DO J=1,K A(J,I:I+P-1)=… END DO END DO Будет ли полезной такая оптимизация?
Векторизация и многоверсионный код. Дополнительная возможность для улучшения производительности – формирование многоверсионного кода, т.е. кода включающего проверки времени выполнения. Примеры случаев, когда это может работать: void sub(int *a,int *b) { int i; for(i=0;i<1000;i++) a[i]=b[i]; } void sub(int *a,int n) { int i; for(i=0;i<1000;i++) a[i]=a[i+n]; } Можно проверить работает ли многоверсионная векторизация для gcc или интеловского компилятора.
Существуют полезные прагмы, которые вы можете использовать для улучшения эффективности векторизации: __declspec(align(n)) – указывает компилятору выравнивать переменную на n-байтовую границу #pragma ivdep Предлагает игнорировать предполагаемую векторную зависимость #pragmavector{aligned|unaligned|always} Определяет как векторизивать цикл #pragma novector Цикл не должен быть векторизован Аналоги для Фортрана !DEC$ IVDEP !DEC$ VECTOR ALWAYS !DEC$ NOVECTOR
Интеловский компилятор пытается объединить цикловые оптимизации, векторизацию, параллелизацию и оценочную модель в одном компоненте для того чтобы достичь наилучшего взаимодействия между этими оптимизациями. Сначала с помощью различных оптимизаций компилятор пытается улучшить работу с памятью, разбивает циклы, переставляет, векторизует их, развертывает, объединяет. Векторизуется только внутренний цикл.