- •Нижегородский государственный университет им. Н.И. Лобачевского
- •Содержание
- •Архитектура GPU NVIDIA
- •Архитектура CPU и GPU
- •Архитектура CPU и GPU
- •Архитектура GPU: общие сведения
- •Архитектура Tesla 8/10
- •Мультипроцессор Tesla 8
- •Мультипроцессор Tesla 10
- •Иерархия памяти Tesla 10
- •Мультипроцессор Fermi
- •Архитектура Fermi
- •Вычислительные возможности
- •Вычислительные возможности
- •Исполнение потоков
- •Основные понятия
- •Взаимодействие потоков
- •Выполнение блоков
- •«Автоматическая масштабируемость»
- •SIMT
- •Пример выполнения варпов
- •Выполнение ветвлений
- •Оптимизация загрузки устройства
- •Иерархия памяти CUDA
- •Типы памяти и скорость доступа
- •Использование локальной памяти
- •Использование константной памяти
- •Использование константной памяти
- •Использование текстурной памяти
- •Использование глобальной памяти
- •Использование глобальной памяти
- •Пример: выделение памяти и копирование вектора
- •Эффективная работа с глобальной памятью
- •Эффективная работа с глобальной памятью
- •Эффективная работа с глобальной памятью
- •Пример доступа к глобальной памяти
- •Пример доступа к глобальной памяти
- •Пример доступа к глобальной памяти
- •Рекомендации по эффективной работе с глобальной памятью
- •Пример
- •Работа с разделяемой памятью
- •Работа с разделяемой памятью
- •Пример
- •Динамически выделяемая разделяемая память
- •Пример
- •Эффективная работа с разделяемой памятью
- •Эффективная работа с разделяемой памятью
- •Доступ к разделяемой памяти без конфликтов банков
- •Доступ к разделяемой памяти с конфликтами банков
- •Пример: параллельная редукция
- •Постановка задачи
- •Иерархическое суммирование
- •Вариант 1
- •Вариант 1
- •Вариант 2
- •Вариант 2
- •Вариант 3
- •Вариант 3
- •Вариант 4
- •Вариант 4
- •Вариант 5
- •Результаты
- •Материалы
Постановка задачи
Дано:
– Массив a0 ,a1 ,...,an 1
–Ассоциативная операция «+» (например, +, *, min, max)
Необходимо найти: A a0 a1 ... an 1
Лимитирующий фактор – доступ к памяти.
Источник материала данного раздела: пример reduction в GPU Computing SDK и соответствующая статья; А.В. Боресков, А.А. Харламов «Иерархия памяти CUDA. Shared-память и ее эффективное использование. Параллельная редукция».
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
51 |
Иерархическое суммирование
Распределяем исходный массив между блоками.
Каждый блок производит иерархическое суммирование в разделяемой памяти.
5 |
3 |
7 |
- 2 |
2 |
0 |
4 |
- 5 |
- 6 |
2 |
1 |
- 3 |
4 |
5 |
- 6 |
3 |
|
8 |
|
5 |
|
2 |
- 1 |
|
|
- 4 |
|
- 2 |
|
9 |
|
- 3 |
|
1 |
3 |
|
|
1 |
|
|
|
|
- 6 |
|
|
|
6 |
|
1 |
4 |
|
0 |
|
|
|
|
1 4
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
52 |
Вариант 1
T h r e a d a
s = 1
s = 2
s = 4
s = 8
0 |
|
|
1 |
2 |
|
|
3 |
4 |
|
|
5 |
6 |
|
|
7 |
8 |
|
|
2 |
1 |
0 |
1 |
1 |
1 |
2 |
1 |
3 |
1 |
|
4 |
1 |
5 |
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
|
|
3 |
7 |
|
|
- 2 |
2 |
|
|
0 |
4 |
|
|
- 5 |
- 6 |
|
2 |
|
1 |
|
|
- 3 |
|
4 |
|
|
|
5 |
- 6 |
|
|
3 |
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
|
3 |
5 |
|
- 2 |
2 |
|
|
0 |
- 1 |
- 5 |
- 4 |
|
2 |
- 2 |
- 3 |
9 |
|
|
5 |
- 3 |
3 |
||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 3 |
3 |
5 |
- 2 |
1 |
|
0 |
- 1 |
- 5 |
- 6 |
|
2 |
- 2 |
- 3 |
6 |
|
5 |
- 3 |
3 |
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 4 |
3 |
5 |
- 2 |
1 |
0 |
- 1 |
- 5 |
0 |
|
2 |
- 2 |
- 3 |
6 |
5 |
- 3 |
3 |
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 4 |
3 |
5 |
- 2 |
1 |
0 |
- 1 |
- 5 |
0 |
2 |
- 2 |
- 3 |
6 |
5 |
- 3 |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
53 |
Вариант 1
__global__ void reduce1 ( int * inData, int * outData )
{
__shared__ int data [BLOCK_SIZE]; int tid = threadIdx.x;
int i = blockIdx.x * blockDim.x + threadIdx.x;
data [tid] |
= inData [i]; |
// |
load into shared memory |
||
__syncthreads |
(); |
|
|
|
|
for ( int s = |
1; s |
< blockDim.x; s *= 2 ) { |
|||
if ( tid |
% (2*s) |
== 0 ) |
// |
heavy branching !!! |
data [tid] += data [tid + s];
__syncthreads ();
}
if ( tid == 0 ) // write result of block reduction outData[blockIdx.x] = data [0];
}
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
54 |
Вариант 2
__global__ void reduce2 ( int * inData, int * outData )
{
__shared__ int data [BLOCK_SIZE];
int tid = |
threadIdx.x; |
|
int i = |
blockIdx.x * blockDim.x + threadIdx.x; |
|
data [tid] = inData [i]; |
// load into shared memory |
|
__syncthreads (); |
|
|
for ( int |
s = 1; s < blockDim.x; s <<= 1 ) |
|
{ |
|
|
int index = 2 * s * tid; if ( index < blockDim.x )
data [index] += data [index + s];
__syncthreads ();
}
if ( tid == 0 ) // write result of block reduction outData [blockIdx.x] = data [0];
}
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
55 |
Вариант 2
Отличие второго варианта от первого лишь в том, какие потоки выполняют (ту же самую) работу.
Во втором варианте почти полностью избавились от ветвления.
Однако на каждой следующей итерации циклов число конфликтов страниц памяти удваивается:
–s = 1: конфликт страниц 2го порядка (index = 1 при tid = 0 и index = 17 при tid = 8);
–s = 2: конфликт страниц 4го порядка (index = 2 при tid = 0, index = 18 при tid = 4, index = 34 при tid = 8, index = 50 при tid = 12);
–…
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
56 |
Вариант 3
Изменим порядок суммирования: будем начинать его не с ближайших, а с удаленных на blockDim.x/2 элементов и будем уменьшать это расстояние вдвое на каждом шаге.
Thread |
0 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
1 |
0 |
1 |
1 |
1 |
2 |
1 |
3 |
1 |
4 |
1 |
5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
a |
5 |
3 |
7 |
- 2 |
2 |
0 |
4 |
- 5 |
- 6 |
2 |
|
1 |
- 3 |
|
4 |
|
5 |
- 6 |
|
3 |
s |
= |
1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- 1 |
5 |
8 |
- 5 |
6 |
5 |
- 2 |
- 2 |
- 6 |
2 |
1 |
- 3 |
4 |
5 |
- 6 |
3 |
s = 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
1 0 |
6 |
|
- 7 |
|
6 |
5 |
- 2 |
- 2 |
- 6 |
2 |
1 |
- 3 |
4 |
5 |
- 6 |
3 |
s = 4
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 1 |
3 |
|
6 |
- 7 |
6 |
5 |
- 2 |
- 2 |
- 6 |
2 |
1 |
- 3 |
4 |
5 |
- 6 |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
s = 8
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 4 |
3 |
6 |
7 |
6 |
5 |
- 2 |
- 2 |
- 6 |
2 |
1 |
- 3 |
4 |
5 |
- 6 |
3 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
57 |
Вариант 3
__global__ void reduce3 ( int * inData, int * outData )
{
__shared__ int data [BLOCK_SIZE]; int tid = threadIdx.x;
int i = blockIdx.x * blockDim.x + threadIdx.x;
data [tid] = inData [i]; __syncthreads ();
for ( int s = blockDim.x / 2; s > 0; s >>= 1 )
{
if ( tid < s )
data [tid] += data [tid + s];
__syncthreads ();
}
if ( tid == 0 )
outData [blockIdx.x] = data [0];
}
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
58 |
Вариант 4
В варианте 3 избавились от конфликтов банков.
На первой итерации половина потоков простаивает, для избежания этого сделаем первое суммирование при загрузке.
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
59 |
Вариант 4
__global__ void reduce4 ( int * inData, int * outData )
{
__shared__ int data [BLOCK_SIZE]; int tid = threadIdx.x;
int i = 2 * blockIdx.x * blockDim.x + threadIdx.x;
data [tid] = inData [i] + inData [i+blockDim.x]; // sum __syncthreads ();
for ( int s = blockDim.x / 2; s > 0; s >>= 1 )
{
if ( tid < s )
data [tid] += data [tid + s];
__syncthreads ();
}
if ( tid == 0 )
outData [blockIdx.x] = data [0];
}
Н. Новгород, 2012 г. |
Исполнение потоков. Иерархия памяти |
60 |