C(J,I)

advertisement
Автоматическое распараллеливание
Многоядерные/многопроцессорные Intel архитектуры
Intel® Pentium® Processor Extreme Edition (2005-2007)
Представил технологию двойного ядра (dual core)
Intel® Xeon® Processor 5100, 5300 Series
Intel® Core™2 Processor Family (2006-)
Появились двухпроцессорные архитектуры. Процессоры поддерживают четыре
ядра(quad-core)
Intel® Xeon® Processor 5200, 5400, 7400 Series
Intel® Core™2 Processor Family (2007-)
Есть семейства в которых количество ядер на процессоре доведено до 6. 2-4
процессора.
Intel® Atom™ Processor Family (2008-)
Процессор с высокой энергоэффективностью.
Intel® Core™i7 Processor Family (2008-)
Hyperthreading технология. Системы с неоднородным доступом к памяти.
10/17/10
Одной из главных особенностей многоядерной
архитектуры является то, что ядра совместно
используют часть подсистемы памяти и шину данных.
10/17/10
Классификация многопроцессорных систем по
использованию памяти
1. Массивно-параллельные компьютеры или системы с распределенной памятью. (MPP
системы).
Каждый процессор полностью автономен.
Существует некоторая коммуникационная среда.
Достоинства:
хорошая масштабируемость.
Недостатки:
медленное межпроцедурное взаимодействие.
2. Системы с общей памятью (SMP системы) .
Все процессоры равноудалены от памяти. Связь с памятью осуществляется через
общую шину данных.
Достоинства:
хорошее межпроцессорное взаимодействие.
Недостатки:
плохая масштабируемость;
большие затраты на синхронизацию подсистем кэшей.
3. Системы с неоднородным доступом к памяти (NUMA) .
Память физически распределена между процессорами. Единое адресное
пространство поддерживается на аппаратном уровне.
Достоинства:
Недостатки:
хорошее межпроцессорное взаимодействие и масштабируемость.
разное время доступа к разным сегментам памяти.
10/17/10
Intel QuickPath Architecture
10/17/10
Нестабильность работы приложений на
многопроцессорных машинах с неоднородным
доступом к памяти.
10/17/10
Плюсы и минусы использования многопоточных
приложений.
++:
Вычислительные ресурсы увеличиваются пропорционально
количеству используемых реальных ядер.
--:
Усложнение разработки.
Необходимость синхронизировать потоки.
Потоки конкурируют за ресурсы.
Создание потоков имеет свою цену.
Вывод: В случае разработки бизнес-приложений четко
осознавайте цели и цену распараллеливания вашей программы.
10/17/10
Автоматическое распараллеливание процесс автоматического преобразования
последовательного программного кода в многопоточный
(multi-threaded), использующий несколько ядер
одновременно. Цель автоматического
распараллеливания – освободить программистов от
тяжелой и нудной ручной работы по разделению
вычислений на различные потоки.
/Qparallel
enable the auto-parallelizer to generate multi-threaded
code for
loops that can be safely executed in parallel
Выгодность автоматического распараллеливания на
простом примере.
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
10/17/10
Хорошо и плохо масштабируемые алгоритмы
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];
}
}
5
4
3
2
1
0
1
2
4
6
8
12
10/17/10
16
Плохо масштабируемые алгоритмы
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];
}
10
8
6
4
2
0
1
2
4
6
8
12
16
10/17/10
Допустимость автоматического распараллеливания
Это преобразование – перестановочная оптимизация
циклической конструкции.
Упорядоченное выполнение итераций =>
неопределенный порядок выполнения итераций.
Необходимое условие – отсутствие любых
зависимостей внутри цикла.
/Qpar-report{0|1|2|3}
control the auto-parallelizer diagnostic level
/Qpar-report3 сообщает причины, по которым
компилятор не распараллеливает тот или иной цикл, в
том числе сообщает, какие зависимости препятствуют
этому.
10/17/10
Выгодность распараллеливания
/Qpar_report3 информирует, если распараллеливание
невыгодно.
C:\test_par.c(27) (col. 1): remark: loop was not parallelized:
insufficient computational work.
Точное определение выгодности таких преобразований
во время компиляции - достаточно тяжелая задача.
Существуют эффекты производительности, которые
сложно оценить, например «эффект первого
прикосновения».
В большинстве случаев компилятор может не иметь
представления о количестве итераций в цикле.
Используйте директивы распараллеливания при
экспериментах с производительностью.
10/17/10
#pragma concurrent – игнорировать предполагаемые
зависимости в следующем цикле.
#pragma concurrent call – вызов функции в следующем
цикле безопасен для параллельного выполнения.
#pragma concurrentize – параллелизовать следующий
цикл.
#pragma no concurrentize - не параллелизовать
следующий цикл.
#pragma prefer concurrent - параллелизовать следующий
цикл, если это безопасно.
#pragma prefer serial – предложить компилятору не
параллелизовать следующий цикл.
#pragma serial – заставить компилятор параллелизовать
следующий цикл.
Автоматическое распараллеливание осуществляется
использованием интерфейса OpenMP.
OpenMP (Open Multi-Processing) – это программный интерфейс,
который поддерживает многоплатформенное многопроцессорное
программирование с общей памятью на C/C++ и Фортране на
многих архитектурах.
Количество потоков, используемых вашим приложением, может
изменяться с помощью установки переменной окружения
OMP_NUM_THREADS
(по умолчанию будут использоваться все доступные ядра).
8 Threads
16 Threads
10/17/10
Распараллеливание цикла выглядит как создание
функции, в которую в качестве аргументов передаются
границы цикла и все используемые объекты. Внутри
функции находится исходный цикл, но его границы
определяются при помощи параметров функции.
Запускаются несколько экземпляров данной функции
в разных потоках с различными значениями границ
цикла.
Т.е. итерационное пространство цикла разбивается
на несколько частей, и каждый поток обрабатывает
свою часть итерационного пространства.
/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
10/17/10
Взаимодействие с другими оптимизациями циклических
конструкций
• Объединение циклов и создание больших циклов.
• Автоматическое распараллеливание.
• Оптимизация цикла в поточной функции в
соответствии с обычными соображениями
(развертка, векторизация, разбиение на несколько
циклов и т.п.).
Эти соображения можно использовать при написании
программы. Стремитесь создавать большие циклы без
зависимостей, т.е. такие, чтобы итерации могли
выполняться в произвольном порядке.
10/17/10
Предвыборка
- загрузка данных из относительно медленной памяти в кэш до того, как эта память
непосредственно потребовалась процессору.
Существуют несколько методов использования этой техники для оптимизации
приложения:
•
Использование встроенных функций (интриниксов);
•
Использование компиляторной опции.
Функция предвыборки определена в xmmintrin.h и имеет форму
#include <xmmintrin.h>
enum _mm_hint { _MM_HINT_T0 = 3,
(L1)
_MM_HINT_T1 = 2,
(L2)
_MM_HINT_T2 = 1,
(L3)
_MM_HINT_NTA = 0 };
void _mm_prefetch(void *p, enum _mm_hint h);
Она подгружает в кэш кэш-линию, начиная с указанного адреса (размер кэш линии
64 байта).
В случае с Фортраном используется вызов CALL mm_prefetch(P,HINT).
10/17/10
Пример использования
DO I=1,N,SEC
JJ=16
DO J=1,K
A(J,I) = A(J,I)+B(J,I)+C(J,I)
#ifdef PERF
IF(JJ==16) THEN
CALL mm_prefetch(A(J,I+SEC),1)
CALL mm_prefetch(B(J,I+SEC),1)
CALL mm_prefetch(C(J,I+SEC),1)
JJ = 0
ENDIF
#endif
JJ=JJ+1
END DO
END DO
10/17/10
Программная предвыборка может быть полезна для
решения проблем типичного C++ кода, одной из проблем
которого является проблема доступа к памяти,
возникающая при обработке различных массивных
списков.
Если вы последовательно обрабатываете список
каких-то объектов, имеет смысл параллельно
подгружать в кэш следующий объект и те объекты, к
которым обрабатывающая функция обращается при
помощи указателей полей данного объекта.
10/17/10
Использование ключей компилятора
/Qopt-prefetch[:n]
enable levels of prefetch insertion, where 0 disables.
n may be 0 through 4 inclusive. Default is 2.
/Qopt-prefetchdisable(DEFAULT) prefetch insertion. Equivalent to
/Qopt-prefetch:0
10/17/10
CEAN (C/C++ Extensions for Array Notations Programming Model)
Описание массивов
Length
Fixed
Variable (C99)
Storage Class
Static
Declaration
static int a[16][128]
Auto
void foo(void) {
int a[16][128];
}
Parameter
void bar(int a[16][128]);
Heap
int (*p2d)[128];
Auto
void foo(int m, int n) {
int a[m][n];
}
Parameter
void bar(int m, int n, int a[m][n]);
Heap
void bar(int m, int n) {
int (*p2d)[n];
}
10/17/10
Описание областей
section_operator :: = [<lower bound>:<length>:<stride>
a[0:3][0:4]
b[0:2:3]
Вы должны использовать расширение –std=c99 (Linux и MAC OS) или
/Qstd=c99
Пример декларации массива и использования секции:
typedef int (*p2d)[128];
p2d p = (p2d) malloc (sizeof(int)*rows*128);
p[0:rows][:]
Большинство C/C++ операторов доступны для работы с секциями.
a[:]*b[:]
// поэлементное умножение
a[3:2][2:2] + b[5:2][5:2]
// сложение матриц
a[0:4]+c
// добавление скаляра к вектору
a[:][:] = b[:][1][:] + c
// матричное присвоение
10/17/10
Прототипы некоторых матричных функций
Function Prototypes
__sec_reduce(fun, identity, a[:])
Descriptions
Generic reduction function. Reduces fun across the
array a[:] using identity as the initial value.
__sec_reduce_add(a[:])
Built-in reduction function. Adds values passed as arrays
__sec_reduce_mul(a[:])
Built-in reduction function. Multiplies values passed as arrays
__sec_reduce_all_zero(a[:])
Built-in reduction function. Tests that array elements are all
zero
Built-in reduction function. Tests that array elements are all
non-zero
Built-in reduction function. Tests for any array element that is
non-zero
Built-in reduction function. Determines the minimum value of
array elements
Built-in reduction function. Determines the maximum value of
array elements
Built-in reduction function. Determines the index of minimum
value of array elements
Built-in reduction function. Determines the index of maximum
value of array elements
__sec_reduce_all_nonzero(a[:])
__sec_reduce_any_nonzero(a[:])
__sec_reduce_min(a[:])
__sec_reduce_max(a[:])
__sec_reduce_min_ind(a[:])
__sec_reduce_max_ind(a[:])
10/17/10
#include <stdio.h>
#include <stdlib.h>
#define N 2000
typedef double (*p2d)[];
void matrix_mul(int n, double a[n][n],
int main() {
p2d a=
(p2d)malloc(N*N*sizeof(double)) ;
p2d b=
(p2d)malloc(N*N*sizeof(double)) ;
p2d c=
(p2d)malloc(N*N*sizeof(double));
double b[n][n],double c[n][n]) {
matrix_mul(N,a,a,a);
matrix_mul(N,a,b,c);
int i,j;
a[:][:] =1;
free(a);
free(b);
free(c);
b[:][:] =-1;
for(i=0;i<n;i++)
}
for(j=0;j<n;j++)
c[i][j]=c[i][j]+
__sec_reduce_add(a[i][:]*b[:][j]);
return;
}
10/17/10
Спасибо за внимание!
10/17/10
Download