Презентацию к лекции Вы можете скачать здесь.
Тенденция в развитии микропроцессоров:
| Последовательное выполнение инструкций | ![]() |
Параллельное выполнение инструкций |
Оптимизирующий компилятор – инструмент, транслирующий исходный код программы в исполняемый модуль, а так же инструмент, оптимизирующий исходный код для получения лучшей производительности. Распараллеливание – трансформация последовательно исполняемой программы в программу, в которой наборы инструкций выполняются одновременно и сохраняется результат работы.
Изначально компьютеры имели одно логическое устройство, исполняющее инструкции последовательно. Программы разрабатывались для последовательного выполнения инструкций и большинство программных языков работали в соответствии с этим принципом. Однако в последствии при развитии архитектуры процессоров много усилий было приложено к тому, чтобы достичь одновременного выполнения нескольких инструкций.
(Если говорить о развитии параллелизма в процессоре, то можно упомянуть такие технологии как
Трансформациями программы из программного языка в инструкции микропроцессора занимается компилятор. Современные компиляторы - это мощные средства разработки, включающие в себя разнообразные интерфейсные и отладочные средства, производящие выполняемые модули для различных архитектур и использующие массу оптимизаций для улучшения производительности программы.
Одна из технологий распараллеливания программы –
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: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 (
MMM0-MMM7 64 битные регистры для работы с целыми числами концепция пакетов, каждый из этих регистров мог хранить
| 2 – 32 битных целых числа |
| 4 - 16 битных |
| 8 - 8 битных |
47 инструкций, которые делятся на несколько груп:
| перемещение данных |
| арифметические |
| сравнения |
| конвертации |
| логические |
| распаковки |
| сдвига |
| пустые инструкции получения состояния |
Эта технология ведет свое начало от инструкций сопроцессора для обработки вещественных чисел или чисел с плавающей точкой. Устройство обработки чисел с плавающей точкой (
Для того, чтобы позволить использовать
| 2 – 32 битных целых числа |
| 4 - 16 битных |
| 8 - 8 битных |
Недостатком была невозможность одновременно работать с целыми и вещественными числами,
SSE (Streaming SIMD Extensions, потоковое SIMD-расширение процессора)
Это набор инструкций, позволяющий работать с множеством данных – SIMD(Single Instruction, Multiple Data, Одна инструкция — множество данных).
Технология EMM64T добавляла к этому набору еще 8 128 битных регистра (xmm8 до xmm15).
AVX новое расширение системы команд (Advanced vector extensions)
AVX предоставляет различные улучшения, новые инструкции и новую схему кодирования машинных кодов.
Размер векторных регистров SIMD увеличивается с 128 до 256 бит.
Регистры YMM0-YMM15
Существующие 128-битные инструкции используют младшую половину YMM регистров.
Технология
Появление этого набора инструкций позволило решить проблему одновременной работы с упакованными целыми и вещественными данными. Теперь упакованные целые обрабатываются с помощью
Помимо векторных регистров
Расширился набор SIMD операций над целыми
Были добавлены инструкции явной предвыборки данных, контроля
Расширения инструкций CPUID для получения информации о процессоре.
Преимущество в производительности достигается в том случае, когда необходимо произвести одну и ту же последовательность действий над разными данными.
Данные различных типов могут быть упакованы в векторные регистры следующим образом:
| Упакованный тип данных | Длина вектора | Число битов на элемент | Область значений типа |
|---|---|---|---|
| 16 | 8 | -2**7 до 2**7-1 | |
| 16 | 8 | 0 до 2**8-1 | |
| signed words | 8 | 16 | -2**15 до 2**15-1 |
| unsigned words | 8 | 16 | 0 до 2**16 |
| signed |
4 | 32 | -2**31 до 2**31-1 |
| unsigned |
4 | 32 | 0 до 2**32-1 |
| signed |
2 | 64 | -2**63 до 2*63-1 |
| unsigned |
2 | 64 | 0 до 2**64-1 |
| 4 | 32 | 2**-126 до 2**127 | |
| 2 | 64 | 2**-1022 до 2**1023 |
Выбор подходящего для вычислений типа данных может существенно сказаться на производительности приложения.
При обсуждении возможного выигрыша от
Optimization with Switches
SIMD – SSE, SSE2, SSE3, SSE4.2 Support
Use –QxW for P4 SIMD data types
64 bit double + 64 bit double = 128 bit register
32 bit + 32 bit + 32 bit + 32 bit (floats) = 128 bit register
Три набора опций для использования процессорно- специфических расширений
–Qx<EXT> например –QxSSE4_1
–arch:<EXT> например –arch:SSE3
–Qax<EXT> например –QaxSSE4_2
–mSSE2Допустимость векторизации
Перестановочные оптимизации допустимы, если не изменяется порядок зависимостей.
Таким образом мы получили критерий допустимости
Простейший вариант – зависимостей в векторизуемом цикле нет.
Зависимости в векторизуемом цикле есть, но их порядок после
Определить существуют или нет зависимости для данного цикла – непростая задача. Компилятор использует оценочные технологии для решения этой проблемы. Дополнительные сложности возникают с указателями в C. Поскольку указатели могут указывать на пересекающиеся области памяти одной из важных задач является доказательство того, что указатели указывают на различные участки памяти.
Упрощает ситуацию для анализа допустимости
/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.
В случае –Qvec-report, которая позволяет получить компиляторную диагностику и понять был ли распознан цикл;
Фортран активно используется в описании
В упрощенном виде:
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
Не может быть векторизован.
По определению зависимость существует если
I и I+1 итерацию, то будем иметь I(I+1:I+1+K) из левой части утверждения (пишем в память) и I(I+K:I+2K) из правой части. Т.е. утверждения на итерациях I и I+1 работают с одной и той-же памятью, возможный путь существует – есть зависимость.
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
|
Предположение:
Цикл можно векторизовать, если дистанция для зависимости Проверяем утверждение с помощью компилятора:
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.
|
Опции группы vec_report (Qvec_report) информируют пользователя о действиях векторизатора.
Многоядерные/многопроцессорные Intel архитектуры
Intel® Pentium® Processor Extreme Edition (2005-2007)
Intel® Xeon® Processor 5100, 5300
Intel® Core™2
Intel® Xeon® Processor 5200, 5400, 7400
Intel® Core™2
Intel® Atom™
Intel® Core™i7
На рынок персональных компьютеров многопроцессорные/многоядерные архитектуры пришли сравнительно недавно. Ядром процессора называется та его часть, которая извлекает из памяти и исполняет инструкции. Изначально процессор содержал одно ядро. Сейчас широко распространены
Одной из главных особенностей многоядерной архитектуры является то, что ядра совместно используют часть подсистемы памяти и шину данных
Производительность современных вычислительных систем определяется в основном скоростью взаимодействия с памятью, поэтому хочется напомнить о особенностях организации подсистемы памяти для многоядерных и многопроцессорных систем.
Приведенная схема описывает примерную организацию памяти в двухядерных процессорах.
Из приведенной схемы понятно, что у двухядерного процессора дублируются практически все ресурсы за исключением кэша второго уровня и шины данных. (Шина данных обрабатывает обращение к памяти.) Т.е. при запуске на ядрах двух приложений узким местом которых является работа с памятью мы можем получить замедление работы обоих приложений, поскольку они будут конкурировать за кэш второго уровня.
Начиная с процессора Nehalem ядра получили свой кэш второго уровня, но совместно пользуются кэшем третьего уровня.
Классификация многопроцессорных систем по использованию памяти
Каждый процессор полностью автономен.
Существует некоторая
| Достоинства: | хорошая масштабируемость |
| Недостатки: | медленное межпроцедурное взаимодействие |
Все процессоры равноудалены от памяти. Связь с памятью осуществляется через общую шину данных.
| Достоинства: | хорошее |
| Недостатки: | плохая масштабируемость |
| большие затраты на синхронизацию подсистем кэшей |
Память физически распределена между процессорами.
| Достоинства: | хорошее |
| Недостатки: | разное время доступа к разным сегментам памяти. |
При создании многопроцессорных систем главным вопросом является управление разными процессорами при решении какой-то общей задачи. Поскольку сейчас главным вопросом ограничивающим быстродействие вычислительных систем является вопрос взаимодействия с памятью, то логично охарактеризовать вычислительные системы по методу работы с памятью.
Если различные процессоры не имеют разделяемой памяти, то это существенно усложняет организацию параллельных вычислений.
Если процессоры могут работать с общей памятью, то возникает вопрос синхронизации подсистем памяти этих процессоров. Как уже упоминалось для ускорения работы с памятью процессоры имеют систему кэшей. Что будет происходить если некоторый адрес памяти будет одновременно присутствовать в нескольких подсистемах памяти и будет модифицирован одним в одном из них? В этом случае необходимо привести в соответствие значения во всех остальных подсистемах. В случае с SMP системами процессор передает информацию об измененном адресе на шину данных. Шина данных в свою очередь передает эту информацию другим процессорам.
Intel QuickPath Architecture
Приведенные соображения о работе памяти верны и в случае многопроцессорной архитектуры. Типичным представителем многопроцесорных архитектур является архитектура i7. Эта архитектура – архитектура с неоднородной памятью. Каждый процессор имеет свою память, доступ к которой наиболее быстрый. Доступ к памяти другого процессора медленнее. Используется QPI соединение между процессорами.
Нестабильность работы приложений на многопроцессорных машинах с неоднородным доступом к памяти
Интересным фактом является то, что приложения на многопроцессорных машинах демонстрируют достаточно нестабильную производительность. Это происходит в том числе и потому, что при выполнении однопоточного приложения оно "кочует" по разным ядрам. Это приводит к тому, что в начальный момент времени память выделяется на одном процессоре и приложение работает с "близкой памятью", но если приложение начинает работать на другом процессоре, то доступ к памяти замедляется.
Эту проблему можно решить с помощью привязки приложения к определенным ядрам. Установить
Плюсы и минусы использования многопоточных приложений
| ++: | Вычислительные ресурсы увеличиваются пропорционально кол-ву используемых реальных ядер. | ||||
| --: |
|
Вывод: В случае разработки бизнес-приложений четко осознавайте цели и цену распараллеливания вашей программы.
В связи с появлением на рынке различных многопроцессорных архитектур существует мода на "параллелизацию" приложений. Однако параллелизация имеет свои плюсы и минусы. Понятно, что существуют приложения, которые обзательно должны быть многопоточными (Вэб сервисы и т.д.). В случае с вычислительными приложениями необходимо решать вопрос о выгодности многопоточности, учитывая следующие плюсы и минусы многопоточных приложений. Плюсы: Вычислительные ресурсы увеличиваются пропорционально количеству используемых реальных ядер (на Nehaleme из-за гиперсрединга на одном реальном ядре находятся два логических). Особенно это важно из-за увеличивающегося размера кэшей разных уровней. Минусы: Усложнение разработки; Проблемы
Автоматическое распараллеливание
Процесс автоматического преобразования последовательного программного кода в многопоточный (multi-threaded), использующий несколько ядер одновременно. Цель автоматического распараллеливания – освободить программистов от тяжелой и нудной ручной работы по разделению вычислений на различные потоки.
/Qparallel
enable the auto-parallelizer to generate multi-threaded code for
loops that can be safely executed in parallel
Компилятор Интел предлагает некоторое компромисное решение. Он под опцией –Qparallel пытается распараллелить циклы с тем чтобы задействовать ресурсы многопроцессорной архитектуры. Плюс такого подхода заключается в том, что можно без дополнительной разработки улучшить производительность приложения.
Выгодность автоматического распараллеливания на простом примере
REAL :: a(1000,1000),b(1000,1000),c(1000,1000)
integer i,j,rep_factor
DO I=1,1000
DO J=1,1000
A(J,I) = I
B(J,I) = I+J
C(J,I) = 0
END DO
END DO
DO rep_factor=1,1000
C=B/A+rep_factor
END DO
END
![]() |
![]() |
Хорошо и плохо масштабируемые алгоритмы
void matrix_mul_matrix(int n,
float C[n][n], float A[n][n],
float B[n][n]) {
int i,j,k;
for (i=0; i<n; i++)
for (j=0; j<n; j++) {
C[i][j]=0;
for(k=0;k<n;k++)
C[i][j]+=A[i][k]*B[k][j];
}
}
|
![]() |
При решении об параллелизации алгоритмов важно учитывать возможные проблемы, которые могут свести на нет все выгоды от параллелизации. Существуют хорошо и плохо масштабируемые алгоритмы. Рассмотрим например программу
Плохо масштабируемые алгоритмы
void matrix_add(int n, float Res[n][n],float A1[n][n], float A2[n][n],
float A3[n][n],float A4[n][n], float A5[n][n], float A6[n][n],
float A7[n][n], float A8[n][n]) {
int i,j;
for (i=0; i<n; i++)
for (j=1; j<n-1; j++)
Res[i][j]=A1[i][j]+A2[i][j]+A3[i][j]+A4[i][j]+
A5[i][j]+A6[i][j]+A7[i][j]+A8[i][j]+
A1[i][j+1]+A2[i][j+1]+A3[i][j+1]+A4[i][j+1]+
A5[i][j+1]+A6[i][j+1]+A7[i][j+1]+A8[i][j+1];
}
|
![]() |
Плохо масштабируемый алгоритм. В данном случае показан пример теста в котором одновременно идет обращение к большому количеству различных объектов в памяти, а сами вычисления достаточно просты. Т.е. "узким" местом данного метода является работа с подсистемой памяти. При увеличении кол-ва ядер увеличивается количество пересылок между процессорами.
Допустимость автоматического распараллеливания
Это преобразование – перестановочная оптимизация циклической конструкции.
Упорядоченное выполнение итераций => неопределенный порядок выполнения итераций.
Необходимое условие – отсутствие любых зависимостей внутри цикла.
/Qpar-report{0|1|2|3}
control the auto-parallelizer diagnostic level
/Qpar-report3 сообщает причины, по которым компилятор не распараллеливает тот или иной цикл, в том числе сообщает какие зависимости препятствуют этому.
Поскольку автопараллелизация работает с циклами, то это цикловая перестановочная оптимизация. Вместо упорядоченного выполнения итераций цикла мы преобразуем цикл так, что порядок выполнения итераций становится неопределенным. Такую оптимизацию можно сделать только при отсутствии зависимостей внутри цикла. Используйте –Qpar-report, чтобы установить причины по которым ваши циклы не параллелизуются. Иногда легко переписать код и удалить зависимость, или если зависимость возникает из-за того, что компилятор не может разрешить проблему разделения памяти, то иногда можно "помочь" компилятору.
Выгодность распараллеливания
/Qpar_report3 информирует, если распараллеливание невыгодно
C:\test_par.c(27) (col. 1): remark: loop was not parallelized: insufficient computational work.
Точное определение выгодности таких преобразований во время компиляции достаточно тяжелая задача.
Существуют эффекты производительности, которые сложно оценить, например "эффект первого прикосновения".
В большинстве случаев компилятор может не иметь представления о количестве итераций в цикле.
Используйте директивы распараллеливания при экспериментах с производительностью.
Запуск потоков имеет свою цену, поэтому невыгодно параллелизовать циклы, если вычислительная работа внутри цикла мала.
С другой стороны есть некоторые эффекты, которые очень сложно просчитать во время компиляции. Например для архитектур с
#pragma concurrent |
– игнорировать предполагаемые зависимости в следующем цикле |
#pragma concurrent call |
– вызов функции в следующем цикле безопасен для параллельного выполнения |
#pragma concurrentize |
– параллелизовать следующий цикл |
#pragma no concurrentize |
- не параллелизовать следующий цикл |
#pragma prefer concurrent |
- параллелизовать следующий цикл, если это безопасно |
#pragma prefer serial |
– предложить компилятору не параллелизовать следующий цикл |
#pragma serial |
– заставить компилятор параллелизовать следующий цикл |
Автоматическое распараллеливание осуществляется использованием интерфейса
Количество потоков, используемых вашим приложением, может изменяться с помощью установки переменной окружения
OMP_NUM_THREADS
(по умолчанию будут использоваться все доступные ядра)
![]() |
8 Threads |
![]() |
16 Threads |
Некоторые технические детали. Автопараллелизация реализована с помощью
Современные компиляторы поддерживают
/Qpar-runtime-control[n]
Control parallelizer to generate runtime check code for effective
automatic parallelization.
n=0 no runtime check based auto-parallelization
n=1 generate runtime check code under conservative mode
(DEFAULT when enabled)
n=2 generate runtime check code under heuristic mode
n=3 generate runtime check code under aggressive mode
Поскольку определение выгодности автопараллелизации достаточно трудная задача, которая зависит от многих факторов, часть которых не может быть известна во время компиляции, то существует возможность создавать многоверсионные приложения, которые содержат проверки времени выполнения и запускают несколько потоков для обработки циклов только при выполнении определенных условий. Опция /Qpar-runtime-control используется для определения уровня использования таких проверок.
Взаимодействие с другими оптимизациями циклических конструкций
Эти соображения можно использовать при написании программы. Стремитесь создавать большие циклы без зависимостей, т.е. такие чтобы итерации могли выполняться в произвольном порядке.
Презентацию к лекции Вы можете скачать здесь.
Тенденция в развитии микропроцессоров:
| Последовательное выполнение инструкций | ![]() |
Параллельное выполнение инструкций |
Оптимизирующий компилятор – инструмент, транслирующий исходный код программы в исполняемый модуль, а так же инструмент, оптимизирующий исходный код для получения лучшей производительности. Распараллеливание – трансформация последовательно исполняемой программы в программу, в которой наборы инструкций выполняются одновременно и сохраняется результат работы.
Изначально компьютеры имели одно логическое устройство, исполняющее инструкции последовательно. Программы разрабатывались для последовательного выполнения инструкций и большинство программных языков работали в соответствии с этим принципом. Однако в последствии при развитии архитектуры процессоров много усилий было приложено к тому, чтобы достичь одновременного выполнения нескольких инструкций.
(Если говорить о развитии параллелизма в процессоре, то можно упомянуть такие технологии как
Трансформациями программы из программного языка в инструкции микропроцессора занимается компилятор. Современные компиляторы - это мощные средства разработки, включающие в себя разнообразные интерфейсные и отладочные средства, производящие выполняемые модули для различных архитектур и использующие массу оптимизаций для улучшения производительности программы.
Одна из технологий распараллеливания программы –
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: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 (
MMM0-MMM7 64 битные регистры для работы с целыми числами концепция пакетов, каждый из этих регистров мог хранить
| 2 – 32 битных целых числа |
| 4 - 16 битных |
| 8 - 8 битных |
47 инструкций, которые делятся на несколько груп:
| перемещение данных |
| арифметические |
| сравнения |
| конвертации |
| логические |
| распаковки |
| сдвига |
| пустые инструкции получения состояния |
Эта технология ведет свое начало от инструкций сопроцессора для обработки вещественных чисел или чисел с плавающей точкой. Устройство обработки чисел с плавающей точкой (
Для того, чтобы позволить использовать
| 2 – 32 битных целых числа |
| 4 - 16 битных |
| 8 - 8 битных |
Недостатком была невозможность одновременно работать с целыми и вещественными числами,
SSE (Streaming SIMD Extensions, потоковое SIMD-расширение процессора)
Это набор инструкций, позволяющий работать с множеством данных – SIMD(Single Instruction, Multiple Data, Одна инструкция — множество данных).
Технология EMM64T добавляла к этому набору еще 8 128 битных регистра (xmm8 до xmm15).
AVX новое расширение системы команд (Advanced vector extensions)
AVX предоставляет различные улучшения, новые инструкции и новую схему кодирования машинных кодов.
Размер векторных регистров SIMD увеличивается с 128 до 256 бит.
Регистры YMM0-YMM15
Существующие 128-битные инструкции используют младшую половину YMM регистров.
Технология
Появление этого набора инструкций позволило решить проблему одновременной работы с упакованными целыми и вещественными данными. Теперь упакованные целые обрабатываются с помощью
Помимо векторных регистров
Расширился набор SIMD операций над целыми
Были добавлены инструкции явной предвыборки данных, контроля
Расширения инструкций CPUID для получения информации о процессоре.
Преимущество в производительности достигается в том случае, когда необходимо произвести одну и ту же последовательность действий над разными данными.
Данные различных типов могут быть упакованы в векторные регистры следующим образом:
| Упакованный тип данных | Длина вектора | Число битов на элемент | Область значений типа |
|---|---|---|---|
| 16 | 8 | -2**7 до 2**7-1 | |
| 16 | 8 | 0 до 2**8-1 | |
| signed words | 8 | 16 | -2**15 до 2**15-1 |
| unsigned words | 8 | 16 | 0 до 2**16 |
| signed |
4 | 32 | -2**31 до 2**31-1 |
| unsigned |
4 | 32 | 0 до 2**32-1 |
| signed |
2 | 64 | -2**63 до 2*63-1 |
| unsigned |
2 | 64 | 0 до 2**64-1 |
| 4 | 32 | 2**-126 до 2**127 | |
| 2 | 64 | 2**-1022 до 2**1023 |
Выбор подходящего для вычислений типа данных может существенно сказаться на производительности приложения.
При обсуждении возможного выигрыша от
Optimization with Switches
SIMD – SSE, SSE2, SSE3, SSE4.2 Support
Use –QxW for P4 SIMD data types
64 bit double + 64 bit double = 128 bit register
32 bit + 32 bit + 32 bit + 32 bit (floats) = 128 bit register
Три набора опций для использования процессорно- специфических расширений
–Qx<EXT> например –QxSSE4_1
–arch:<EXT> например –arch:SSE3
–Qax<EXT> например –QaxSSE4_2
–mSSE2Допустимость векторизации
Перестановочные оптимизации допустимы, если не изменяется порядок зависимостей.
Таким образом мы получили критерий допустимости
Простейший вариант – зависимостей в векторизуемом цикле нет.
Зависимости в векторизуемом цикле есть, но их порядок после
Определить существуют или нет зависимости для данного цикла – непростая задача. Компилятор использует оценочные технологии для решения этой проблемы. Дополнительные сложности возникают с указателями в C. Поскольку указатели могут указывать на пересекающиеся области памяти одной из важных задач является доказательство того, что указатели указывают на различные участки памяти.
Упрощает ситуацию для анализа допустимости
/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.
В случае –Qvec-report, которая позволяет получить компиляторную диагностику и понять был ли распознан цикл;
Фортран активно используется в описании
В упрощенном виде:
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
Не может быть векторизован.
По определению зависимость существует если
I и I+1 итерацию, то будем иметь I(I+1:I+1+K) из левой части утверждения (пишем в память) и I(I+K:I+2K) из правой части. Т.е. утверждения на итерациях I и I+1 работают с одной и той-же памятью, возможный путь существует – есть зависимость.
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
|
Предположение:
Цикл можно векторизовать, если дистанция для зависимости Проверяем утверждение с помощью компилятора:
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.
|
Опции группы vec_report (Qvec_report) информируют пользователя о действиях векторизатора.
Многоядерные/многопроцессорные Intel архитектуры
Intel® Pentium® Processor Extreme Edition (2005-2007)
Intel® Xeon® Processor 5100, 5300
Intel® Core™2
Intel® Xeon® Processor 5200, 5400, 7400
Intel® Core™2
Intel® Atom™
Intel® Core™i7
На рынок персональных компьютеров многопроцессорные/многоядерные архитектуры пришли сравнительно недавно. Ядром процессора называется та его часть, которая извлекает из памяти и исполняет инструкции. Изначально процессор содержал одно ядро. Сейчас широко распространены
Одной из главных особенностей многоядерной архитектуры является то, что ядра совместно используют часть подсистемы памяти и шину данных
Производительность современных вычислительных систем определяется в основном скоростью взаимодействия с памятью, поэтому хочется напомнить о особенностях организации подсистемы памяти для многоядерных и многопроцессорных систем.
Приведенная схема описывает примерную организацию памяти в двухядерных процессорах.
Из приведенной схемы понятно, что у двухядерного процессора дублируются практически все ресурсы за исключением кэша второго уровня и шины данных. (Шина данных обрабатывает обращение к памяти.) Т.е. при запуске на ядрах двух приложений узким местом которых является работа с памятью мы можем получить замедление работы обоих приложений, поскольку они будут конкурировать за кэш второго уровня.
Начиная с процессора Nehalem ядра получили свой кэш второго уровня, но совместно пользуются кэшем третьего уровня.
Классификация многопроцессорных систем по использованию памяти
Каждый процессор полностью автономен.
Существует некоторая
| Достоинства: | хорошая масштабируемость |
| Недостатки: | медленное межпроцедурное взаимодействие |
Все процессоры равноудалены от памяти. Связь с памятью осуществляется через общую шину данных.
| Достоинства: | хорошее |
| Недостатки: | плохая масштабируемость |
| большие затраты на синхронизацию подсистем кэшей |
Память физически распределена между процессорами.
| Достоинства: | хорошее |
| Недостатки: | разное время доступа к разным сегментам памяти. |
При создании многопроцессорных систем главным вопросом является управление разными процессорами при решении какой-то общей задачи. Поскольку сейчас главным вопросом ограничивающим быстродействие вычислительных систем является вопрос взаимодействия с памятью, то логично охарактеризовать вычислительные системы по методу работы с памятью.
Если различные процессоры не имеют разделяемой памяти, то это существенно усложняет организацию параллельных вычислений.
Если процессоры могут работать с общей памятью, то возникает вопрос синхронизации подсистем памяти этих процессоров. Как уже упоминалось для ускорения работы с памятью процессоры имеют систему кэшей. Что будет происходить если некоторый адрес памяти будет одновременно присутствовать в нескольких подсистемах памяти и будет модифицирован одним в одном из них? В этом случае необходимо привести в соответствие значения во всех остальных подсистемах. В случае с SMP системами процессор передает информацию об измененном адресе на шину данных. Шина данных в свою очередь передает эту информацию другим процессорам.
Intel QuickPath Architecture
Приведенные соображения о работе памяти верны и в случае многопроцессорной архитектуры. Типичным представителем многопроцесорных архитектур является архитектура i7. Эта архитектура – архитектура с неоднородной памятью. Каждый процессор имеет свою память, доступ к которой наиболее быстрый. Доступ к памяти другого процессора медленнее. Используется QPI соединение между процессорами.
Нестабильность работы приложений на многопроцессорных машинах с неоднородным доступом к памяти
Интересным фактом является то, что приложения на многопроцессорных машинах демонстрируют достаточно нестабильную производительность. Это происходит в том числе и потому, что при выполнении однопоточного приложения оно "кочует" по разным ядрам. Это приводит к тому, что в начальный момент времени память выделяется на одном процессоре и приложение работает с "близкой памятью", но если приложение начинает работать на другом процессоре, то доступ к памяти замедляется.
Эту проблему можно решить с помощью привязки приложения к определенным ядрам. Установить
Плюсы и минусы использования многопоточных приложений
| ++: | Вычислительные ресурсы увеличиваются пропорционально кол-ву используемых реальных ядер. | ||||
| --: |
|
Вывод: В случае разработки бизнес-приложений четко осознавайте цели и цену распараллеливания вашей программы.
В связи с появлением на рынке различных многопроцессорных архитектур существует мода на "параллелизацию" приложений. Однако параллелизация имеет свои плюсы и минусы. Понятно, что существуют приложения, которые обзательно должны быть многопоточными (Вэб сервисы и т.д.). В случае с вычислительными приложениями необходимо решать вопрос о выгодности многопоточности, учитывая следующие плюсы и минусы многопоточных приложений. Плюсы: Вычислительные ресурсы увеличиваются пропорционально количеству используемых реальных ядер (на Nehaleme из-за гиперсрединга на одном реальном ядре находятся два логических). Особенно это важно из-за увеличивающегося размера кэшей разных уровней. Минусы: Усложнение разработки; Проблемы
Автоматическое распараллеливание
Процесс автоматического преобразования последовательного программного кода в многопоточный (multi-threaded), использующий несколько ядер одновременно. Цель автоматического распараллеливания – освободить программистов от тяжелой и нудной ручной работы по разделению вычислений на различные потоки.
/Qparallel
enable the auto-parallelizer to generate multi-threaded code for
loops that can be safely executed in parallel
Компилятор Интел предлагает некоторое компромисное решение. Он под опцией –Qparallel пытается распараллелить циклы с тем чтобы задействовать ресурсы многопроцессорной архитектуры. Плюс такого подхода заключается в том, что можно без дополнительной разработки улучшить производительность приложения.
Выгодность автоматического распараллеливания на простом примере
REAL :: a(1000,1000),b(1000,1000),c(1000,1000)
integer i,j,rep_factor
DO I=1,1000
DO J=1,1000
A(J,I) = I
B(J,I) = I+J
C(J,I) = 0
END DO
END DO
DO rep_factor=1,1000
C=B/A+rep_factor
END DO
END
![]() |
![]() |
Хорошо и плохо масштабируемые алгоритмы
void matrix_mul_matrix(int n,
float C[n][n], float A[n][n],
float B[n][n]) {
int i,j,k;
for (i=0; i<n; i++)
for (j=0; j<n; j++) {
C[i][j]=0;
for(k=0;k<n;k++)
C[i][j]+=A[i][k]*B[k][j];
}
}
|
![]() |
При решении об параллелизации алгоритмов важно учитывать возможные проблемы, которые могут свести на нет все выгоды от параллелизации. Существуют хорошо и плохо масштабируемые алгоритмы. Рассмотрим например программу
Плохо масштабируемые алгоритмы
void matrix_add(int n, float Res[n][n],float A1[n][n], float A2[n][n],
float A3[n][n],float A4[n][n], float A5[n][n], float A6[n][n],
float A7[n][n], float A8[n][n]) {
int i,j;
for (i=0; i<n; i++)
for (j=1; j<n-1; j++)
Res[i][j]=A1[i][j]+A2[i][j]+A3[i][j]+A4[i][j]+
A5[i][j]+A6[i][j]+A7[i][j]+A8[i][j]+
A1[i][j+1]+A2[i][j+1]+A3[i][j+1]+A4[i][j+1]+
A5[i][j+1]+A6[i][j+1]+A7[i][j+1]+A8[i][j+1];
}
|
![]() |
Плохо масштабируемый алгоритм. В данном случае показан пример теста в котором одновременно идет обращение к большому количеству различных объектов в памяти, а сами вычисления достаточно просты. Т.е. "узким" местом данного метода является работа с подсистемой памяти. При увеличении кол-ва ядер увеличивается количество пересылок между процессорами.
Допустимость автоматического распараллеливания
Это преобразование – перестановочная оптимизация циклической конструкции.
Упорядоченное выполнение итераций => неопределенный порядок выполнения итераций.
Необходимое условие – отсутствие любых зависимостей внутри цикла.
/Qpar-report{0|1|2|3}
control the auto-parallelizer diagnostic level
/Qpar-report3 сообщает причины, по которым компилятор не распараллеливает тот или иной цикл, в том числе сообщает какие зависимости препятствуют этому.
Поскольку автопараллелизация работает с циклами, то это цикловая перестановочная оптимизация. Вместо упорядоченного выполнения итераций цикла мы преобразуем цикл так, что порядок выполнения итераций становится неопределенным. Такую оптимизацию можно сделать только при отсутствии зависимостей внутри цикла. Используйте –Qpar-report, чтобы установить причины по которым ваши циклы не параллелизуются. Иногда легко переписать код и удалить зависимость, или если зависимость возникает из-за того, что компилятор не может разрешить проблему разделения памяти, то иногда можно "помочь" компилятору.
Выгодность распараллеливания
/Qpar_report3 информирует, если распараллеливание невыгодно
C:\test_par.c(27) (col. 1): remark: loop was not parallelized: insufficient computational work.
Точное определение выгодности таких преобразований во время компиляции достаточно тяжелая задача.
Существуют эффекты производительности, которые сложно оценить, например "эффект первого прикосновения".
В большинстве случаев компилятор может не иметь представления о количестве итераций в цикле.
Используйте директивы распараллеливания при экспериментах с производительностью.
Запуск потоков имеет свою цену, поэтому невыгодно параллелизовать циклы, если вычислительная работа внутри цикла мала.
С другой стороны есть некоторые эффекты, которые очень сложно просчитать во время компиляции. Например для архитектур с
#pragma concurrent |
– игнорировать предполагаемые зависимости в следующем цикле |
#pragma concurrent call |
– вызов функции в следующем цикле безопасен для параллельного выполнения |
#pragma concurrentize |
– параллелизовать следующий цикл |
#pragma no concurrentize |
- не параллелизовать следующий цикл |
#pragma prefer concurrent |
- параллелизовать следующий цикл, если это безопасно |
#pragma prefer serial |
– предложить компилятору не параллелизовать следующий цикл |
#pragma serial |
– заставить компилятор параллелизовать следующий цикл |
Автоматическое распараллеливание осуществляется использованием интерфейса
Количество потоков, используемых вашим приложением, может изменяться с помощью установки переменной окружения
OMP_NUM_THREADS
(по умолчанию будут использоваться все доступные ядра)
![]() |
8 Threads |
![]() |
16 Threads |
Некоторые технические детали. Автопараллелизация реализована с помощью
Современные компиляторы поддерживают
/Qpar-runtime-control[n]
Control parallelizer to generate runtime check code for effective
automatic parallelization.
n=0 no runtime check based auto-parallelization
n=1 generate runtime check code under conservative mode
(DEFAULT when enabled)
n=2 generate runtime check code under heuristic mode
n=3 generate runtime check code under aggressive mode
Поскольку определение выгодности автопараллелизации достаточно трудная задача, которая зависит от многих факторов, часть которых не может быть известна во время компиляции, то существует возможность создавать многоверсионные приложения, которые содержат проверки времени выполнения и запускают несколько потоков для обработки циклов только при выполнении определенных условий. Опция /Qpar-runtime-control используется для определения уровня использования таких проверок.
Взаимодействие с другими оптимизациями циклических конструкций
Эти соображения можно использовать при написании программы. Стремитесь создавать большие циклы без зависимостей, т.е. такие чтобы итерации могли выполняться в произвольном порядке.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.