Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

CUDA_full / L03_Threads_Memory

.pdf
Скачиваний:
23
Добавлен:
27.03.2015
Размер:
1.11 Mб
Скачать

Использование глобальной памяти

Выделение и освобождение памяти на устройстве:

cudaError_t cudaMalloc (void** devPtr, size_t count) –

выделяет память на устройстве и возвращает указатель на нее;

cudaError_t cudaFree (void* devPtr) – освобождает память на устройстве.

Копирование данных между хостом и устройством:

cudaError_t cudaMemcpy (void* dst, const void* src, size_t count, enum cudaMemcpyKind kind),

kind может принимать значения cudaMemcpyHostToDevice и cudaMemcpyDeviceToHost.

cudaMemcpyAsync – неблокирующий вариант.

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

31

 

 

Пример: выделение памяти и копирование вектора

Выделение памяти на хосте и устройстве: int n = 1000;

float * a = new float[n], * a_gpu; cudaMalloc((void**)&a_gpu, n *

sizeof(float));

Копирование с хоста на устройтво: cudaMemcpy(a_gpu, a, n * sizeof(float),

cudaMemcpyHostToDevice);

Копирование с устройства на хост: cudaMemcpy(a, a_gpu, n * sizeof(float),

cudaMemcpyDeviceToHost);

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

32

 

 

Эффективная работа с глобальной памятью

L1/L2 кэш для глобальной памяти появился в архитектуре

Fermi.

Латентность 400-600 тактов (без учета кэша или в случае кэш-промаха).

Оптимизация работы с глобальной памятью обычно имеет очень большое значение.

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

33

 

 

Эффективная работа с глобальной памятью

Доступ в глобальную память осуществляется полуварпами.

Каждый поток обращается к 32-, 64или 128-битным словам.

При выполнении определенных условий происходит

объединение запросов (коалесцирование, coalescing) к

памяти всех потоков полуварпа в одну операцию доступа к непрерывному блоку памяти и выполнение их одной инструкцией.

В противном случае доступ полуварпа к памяти разбивается на несколько последовательных доступов к непрерывным блокам памяти.

Условия для объединения запросов зависят от вычислительных возможностей, постепенно они становятся

все более мягкими.

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

34

 

 

Эффективная работа с глобальной памятью

Условия, необходимые для объединения запросов:

Доступ в пределах одного последовательного участка памяти определенного размера.

Выровненность адресов, с которыми работает каждый поток, по размеру типа. Выровненность автоматически обеспечивается для встроенных векторных типов, для обеспечения выровненности структур используется

__align__ (newsize) при их объявлении.

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

35

 

 

Пример доступа к глобальной памяти

[NVIDIA CUDA C Programming Guide v. 4.0]

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

36

 

 

Пример доступа к глобальной памяти

[NVIDIA CUDA C Programming Guide v. 4.0]

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

37

 

 

Пример доступа к глобальной памяти

[NVIDIA CUDA C Programming Guide v. 4.0]

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

38

 

 

Рекомендации по эффективной работе с глобальной памятью

За счет объединения запросов можно добиться значительного уменьшения времени на доступ к глобальной памяти.

Объединение запросов заведомо невозможно, когда адреса, по которым обращаются потоки одного полуварпа, расположены далеко друг от друга.

Для обеспечения объединения запросов обычно стоит предпочитать регулярные структуры данных нерегулярным.

В случае независимой обработки вместо массива структур обычно лучше использовать массивы отдельных компонент.

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

39

 

 

Пример

/* Одна итерация метода Якоби для системы размера n на n: x0

– текущее приближение, x1 – следующее, матрица a хранится

по строкам, f – правая часть. Каждый поток вычисляет свой

элемент вектора x1, общее число потоков равно n. */

__global__ void kernel (float * a, float * f, float * x0, float * x1, int n)

{

int idx = blockIdx.x * blockDim.x + threadIdx.x; int ia = n * idx;

float sum = 0.0f;

for (int i = 0; i < n; i++)

 

 

sum += a[ia + i] * x0[i]; /* плохой вариант

 

 

доступа к a, лучше хранить ее по столбцам */

 

float alpha = 1.0f / a[ia + idx];

 

x1[idx] = x0[idx] + alpha * (f[idx] - sum);

}

 

Использованы материалы: А.В. Боресков, А.А. Харламов «Архитектура и

 

 

программирование массивно-параллельных вычислительных систем»

 

 

 

Н. Новгород, 2012 г.

Исполнение потоков. Иерархия памяти

40

 

 

Соседние файлы в папке CUDA_full