1. Лекция 3Векторизация кода (Code vectorization: SSE/AVX)
КурносовМихаил Георгиевич
E-mail: mkurnosov@gmail.com
WWW: www.mkurnosov.net
Курс “Высокопроизводительные вычислительные системы”
Сибирский государственный университет телекоммуникаций и информатики (Новосибирск)
Осенний семестр, 2014
2. Instruction Level Parallelism
2
Архитектурные решения для обеспечения параллельного выполнения коротких последовательностей инструкций
Вычислительный конвейер(Pipeline)–совмещение (overlap)во времени выполнения инструкций
Суперскалярноевыполнение (Superscalar) –выдача и выполнение нескольких инструкций за такт (CPI < 1)
Внеочередное выполнение(Out-of-order execution)– динамическая выдача инструкций на выполнение по готовности их данных
3. Instruction Level Parallelism
3
Архитектурные решения для обеспечения параллельного выполнения коротких последовательностей инструкций
Вычислительный конвейер(Pipeline)–совмещение (overlap)во времени выполнения инструкций
Суперскалярноевыполнение (Superscalar) –выдача и выполнение нескольких инструкций за такт (CPI < 1)
Внеочередное выполнение(Out-of-order execution)– динамическая выдача инструкций на выполнение по готовности их данных
Каждый подход имеет свои плюсы и минусы
Существует ли альтернатива этим подходам?
4. Векторные процессоры(SIMD)
4
Векторный процессор (Vector processor)– это процессор, поддерживающий на уровне системы команд операции для работы с одномерными массивами –векторами(vector)
X
Y
X + Y
+
Скалярный процессор(Scalar processor)
X0
X1
…
Xn-1
+
Y0
Y1
…
Yn-1
X0 + Y0
X1 + Y1
…
Xn-1+ Yn-1
Векторный процессор(Vector processor)
addZ, X, Y
add.vZ[0:n-1], X[0:n-1], Y[0:n-1]
5. Vector CPU vs. Scalar CPU
5
Поэлементное суммирование двух массивов из 10 чисел
fori= 1 to10 do // cmp, jle, inc
IF–Instruction Fetch (next)
ID–Instruction Decode
Load Operand1
LoadOperand2
AddOperand1 Operand2
StoreResult
end for
IF–Instruction Fetch
ID–Instruction Decode
Load Operand1[0:9]
LoadOperand2[0:9]
AddOperand1[0:9] Operand2[0:9]
StoreResult
Скалярный процессор(Scalar processor)
Векторный процессор(Vector processor)
6. Vector CPU vs. Scalar CPU
6
Поэлементное суммирование двух массивов из 10 чисел
fori= 1 to10 do
IF–Instruction Fetch (next)
ID–Instruction Decode
Load Operand1
LoadOperand2
AddOperand1 Operand2
StoreResult
end for
IF–Instruction Fetch
ID–Instruction Decode
Load Operand1[0:9]
LoadOperand2[0:9]
AddOperand1[0:9] Operand2[0:9]
StoreResult
Скалярный процессор(Scalar processor)
Векторный процессор(Vector processor)
Меньше преобразований адресов
Меньше IF, ID
Меньше конфликтов конвейера, ошибок предсказания переходов
Эффективнее доступ к памяти (2 выборки vs. 20)
Операция над операндами выполняется параллельно
Уменьшился размер кода
7. Производительность векторного процессора
7
Факторы влияющие на производительность векторного процессора
Доля кода, который может быть выражен в векторной форме (с использованием векторных инструкций)
Длина вектора (векторного регистра)
Латентность векторной инструкции (Vector startup latency) – начальная задержка конвейера при обработке векторной инструкции
Количество векторных регистров
Количество векторных модулей доступа к памяти (load-store)
…
8. Виды векторных процессоров
8
Векторные процессоры память-память (Memory-memory vector processor)–векторы размещены в оперативной памяти, все векторные операции память-память
Примеры:
CDC STAR-100 (1972, вектор 65535 элементов)
Texas Instruments ASC (1973)
Векторные процессоры регистр-регистр (Register-vector vector processor)–векторы размещены в векторных регистрах, все векторные операции выполняются между векторными регистрами
Примеры:практически все векторные системы начиная с конца 1980-х–Cray, Convex, Fujitsu, Hitachi, NEC, …
SIMD –Single Instruction Stream,Multiple Data Stream
16. Intel MMX
16
1997, Intel Pentium MMX
MMX –набор SIMD-инструкции обработки целочисленныхвекторов длиной 64 бит
8 виртуальных регистров mm0, mm1, …, mm7 – ссылки на физические регистры x87 FPU (ОС не требуется сохранять/восстанавливать регистрыmm0, …, mm7при переключении контекста)
Типы векторов: 8 x 1 char, 4 x short int, 2 x int
MMX-инструкции разделяли x87 FPU сFP-инструкциями –требовалось оптимизировать поток инструкций (отдавать предпочтение инструкциям одного типа)
17. Intel SSE
17
1999, Pentium III
8 векторных регистров шириной 128 бит: %xmm0, %xmm1, …, %xmm7
Типы данных: float(4 элемента на вектор)
70 инструкций:команды пересылки, арифметические команды, команды сравнения, преобразования типов, побитовые операции
Инструкции явной предвыборки данных, контроля кэширования данных и контроля порядка операций сохранения
float
float
float
float
XMM0
float
float
float
float
XMM1
mulps%xmm1, %xmm0 // xmm0 = xmm0 * xmm1
18. Intel SSE
18
1999, Pentium III
8 векторных регистров шириной 128 бит: %xmm0, %xmm1, …, %xmm7
Типы данных: float(4 элемента на вектор)
70 инструкций:команды пересылки, арифметические команды, команды сравнения, преобразования типов, побитовые операции
Инструкции явной предвыборки данных, контроля кэширования данных и контроля порядка операций сохранения
float
float
float
float
XMM0
float
float
float
float
XMM1
mulps%xmm1, %xmm0 // xmm0 = xmm0 * xmm1
Один из разработчиков расширения SSE– В.М. Пентковский(1946 –2012г.)
До переход в Intel являлся сотрудником Новосибирского филиала ИТМиВТ(программное обеспечение многопроцессорных комплексов Эльбрус 1 и 2, язык Эль-76, процессор Эль-90, …)
JagannathKeshavaand Vladimir Pentkovski: Pentium III Processor Implementation Tradeoffs. // Intel Technology Journal. —1999. —Т. 3. —№ 2.
Srinivas K. Raman, Vladimir M. Pentkovski, JagannathKeshava: Implementing Streaming SIMD Extensions on the Pentium III Processor. // IEEE Micro, Volume 20, Number 1, January/February 2000: 47-57 (2000)
19. Intel SSE2
2001, Pentium 4, IA32, x86-64 (Intel 64, 2004)
16векторных регистров шириной 128 бит: %xmm0, %xmm1, …, %xmm7; %xmm8, …, %xmm15
Добавлено 144 инструкциик 70 инструкциям SSE
По сравнению с SSE сопроцессор FPU (x87) обеспечивает более точный результат при работе с вещественными числами
char
char
char
char
char
…
char
16x char
1 x 128-bit int
8x short int
4x float | int
2 x double
shortint
shortint
…
shortint
float
float
float
float
double
double
128-bit integer
26. SSE-инструкции копирования данных
26
MOVSS: Copy a single floating-point data
MOVLPS: Сopy2 floating-point data (low packed)
MOVHPS: Сopy2 floating-point data (high packed)
MOVAPS: Сopyaligned 4 floating-point data (fast)
MOVUPS: Сopyunaligned 4 floating-point data (slow)
MOVHLPS: Сopy2 high elements to low position
MOVLHPS: Сopy2 low elements to high position
При копировании данных из памяти в векторный регистр (и наоборот) рекомендуется чтобы адрес был выровнен на границу в 16 байт
29. Использование инструкций SSE
29
Простота использования
Лучшая управляемость (полный контроль)
Ассемблерные вставки
Встроенные функции компилятора (Intrinsic)
C++ классы
Автоматическая векторизация компилятора
30. Автовекторизациякомпилятором
30
Сlang(LLVM) –векторизация включена по умолчанию
$ clang -mllvm-force-vector-width=8 ...
$ opt -loop-vectorize-force-vector-width=8 ...
$ clang -fslp-vectorize-aggressive ./prog.c
Visual C++ 2012–векторизация включена по умолчанию (при использовании опции /O2, подробный отчет формируется опцией /Qvec-report)
Intel C++ Compiler –векторизация включена по умолчанию (при использовании опции /O2, -O2, подробный отчет формируется опцией /Qvec-report, -vec-report)
Oracle Solaris Studio –векторизация включается совместным использованием опций -xvector=simdи –xO3
31. Автовекторизациякомпилятором
31
GNU GCC –векторизация включается при использовании опции -O3 или -ftree-vectorize
Векторизация для PowerPC (набор инстуркцийAltiVec) включается опцией –maltivec
Векторизация для ARM NEON: -mfpu=neon -mfloat-abi=softfpили-mfloat-abi=hardhttp://gcc.gnu.org/projects/tree-ssa/vectorization.html
39. SSE Intrinsics(builtinfunctions)
Intrinsaics–набор встроенных функций и типов данных, поддерживаемых компилятором, для предоставления высокоуровневого доступа к SSE-инструкциям
Компилятор самостоятельно распределяет XMM/YMM регистры, принимает решение о способе загрузки данных из памяти (проверяет выравнен адрес или нет) и т.п.
Заголовочныефайлы:
#include<mmintrin.h>/*MMX*/
#include<xmmintrin.h>/*SSE,нужентакжеmmintrin.h*/
#include<emmintrin.h>/*SSE2,нужентакжеxmmintrin.h*/
#include<pmmintrin.h>/*SSE3,нужентакжеemmintrin.h*/
#include<smmintrin.h>/*SSE4.1*/
#include<nmmintrin.h>/*SSE4.2*/
#include<immintrin.h>/*AVX*/
44. Выравнивание адресов памяти
44
Выравнивание адресов памятиХранимые в памяти операнды SSE-инструкций должны быть размещены по адресу выровненному на границу в 16 байт
/* Microsoft Visual C/C++ Compiler */
/* Определение статического массива */
__declspec(align(16)) floatA[N];
/*
* Динамическое выделение памяти * с заданным выравниванием адреса
*/
#include <malloc.h>
void*_aligned_malloc(size_tsize, size_talignment);
void_aligned_free(void*memblock);
45. Выравнивание адресов памяти
45
Выравнивание адресов памятиХранимые в памяти операнды SSE-инструкций должны быть размещены по адресу выровненному на границу в 16 байт
/* GNU GCC */
/* Определение статического массива */
float A[N]__attribute__((aligned(16)));
/* Динамическое выделение памяти с заданным выравниванием */
#include <malloc.h>
void*_mm_malloc(size_tsize, size_talign)
void_mm_free(void*p)
#include <stdlib.h>
intposix_memalign(void **memptr, size_talignment,
size_tsize);
46. Intrinsic Name
Operation
CorrespondingIntel® SSE Instruction
__m128 _mm_load_ss(float * p)
Load the low value and clear the three high values
MOVSS
__m128 _mm_load1_ps(float * p)
Load one value into all four words
MOVSS + Shuffling
__m128 _mm_load_ps(float * p)
Load four values, address aligned
MOVAPS
__m128 _mm_loadu_ps(float * p)
Load four values, address unaligned
MOVUPS
__m128 _mm_loadr_ps(float * p)
Load four values in reverse
MOVAPS + Shuffling
Функции копирования данных
46
#include <xmmintrin.h> /* SSE */
Intel® C++ Compiler XE 13.1 User and Reference Guides
47. Intrinsic Name
Operation
CorrespondingIntel® SSE Instruction
__m128d _mm_load_pd(double const*dp)
Loads two DP FP values
MOVAPD
__m128d _mm_load1_pd(double const*dp)
Loads a single DP FP value, copying to both elements
MOVSD+ shuffling
__m128d _mm_loadr_pd(double const*dp)
Loads two DP FP values in reverse order
MOVAPD+ shuffling
__m128d _mm_loadu_pd(double const*dp)
Loads two DP FP values
MOVUPD
__m128d _mm_load_sd(double const*dp)
Loads a DP FP value, sets upper DP FP to zero
MOVSD
Функции копирования данных
47
#include <emmintrin.h> /* SSE2 */
Intel® C++ Compiler XE 13.1 User and Reference Guides
48. Функции копирования данных
48
Intel® C++ Compiler XE 13.1 User and Reference Guides
4.0
3.0
2.0
1.0
t
t = _mm_set_ps(4.0, 3.0, 2.0, 1.0);
1.0
1.0
1.0
1.0
t
t = _mm_set1_ps(1.0);
0.0
0.0
0.0
1.0
t
t = _mm_set_ss(1.0);
0.0
0.0
0.0
0.0
t
t = _mm_setzero_ps();