Программирование графических процессоров Лабораторная 2
Состав работы
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Работа представляет собой rar архив с файлами (распаковать онлайн), которые открываются в программах:
- Microsoft Word
Описание
Задание
1. Прочитайте главу из теоретического материала "Разделяемая память" и ответьте на контрольные вопросы (ответы на контрольные вопросы не нужно включать в отчёт по лабораторной работе).
2. Оптимизируйте алгоритмы, реализованные в лабораторной работе №1 при помощи разделяемой памяти.
3. Постройте графики зависимости времени выполнения алгоритма от размера матрицы и вектора (Размеры матрицы 1000x500, 1000x1000, 1500x1000, 2000x1000, 2000x1500, 2500x1500, 2500x2000).
4. Проанализируйте, реализованные алгоритмы при помощи утилиты nvprof на эффективность доступа к глобальной памяти.
Методические указания по выполнению лабораторной работы
Разделяемая память – это своего рода кэш. Ускорения от использования разделяемой памяти можно достичь только если к каким-то данным происходит многократное обращение. Тогда переместив их в разделяемую память из глобальной можно сократить время затраченное на обращение в память за счёт высокой скорости разделяемой памяти.
Рассмотрим схему параллельного умножения матрицы на вектор.
Рис. 1 – схема умножения матрицы на вектор
Каждая нить берёт по одной строке матрицы и умножает попарно элементы строки на элементы вектора. Все нити используют один и тот же вектор. Скопировав вектор в разделяемую память можно получить ускорения выполнения алгоритма. Но проблема разделяемой памяти в том, что её объём очень мал по сравнению с объёмом глобальной памяти. Поэтому нужно предусмотреть ситуацию когда вектор полностью в память не помещается.
Учитывая, что разделяемой памяти может не хватить нужно модифицировать алгоритм таким образом чтобы часть вектора копировалась в разделяемую память. Нити умножали часть строки на часть вектора, расположенного в разделяемой памяти. Затем копировали следующую часть вектора в разделяемую память и умножали элементы следующей части строки и т.д. как показано на рисунке 2.
Рис. 2 – умножение нитью строки на вектор с копированием частей вектора в разделяемую память
Копирование частей вектора можно производить параллельно, каждая нить может копировать в разделяемую память один или несколько элементов. Таким образом псевдокод алгоритма будет выглядеть следующим образом:
//Выделяем разделяемую память
//Количество элементов будет равно количеству нитей в блоке.
__shared__ shared_vector[THREAD_PER_BLOCK];
//k - номер части вектора. +1 потому что M не обязательно делится нацело
for(k = 0; k < (M / THREAD_PER_BLOCK) + 1; ++k) {
//p - реальный номер элемента вектора
//который будет скопирован нитью в разделяемую память
p = threadIdx.x + THREAD_PER_BLOCK * k;
//Проверка на выход за пределы вектора
if(p < M) {
//Копирование из глобальной памяти в разделяемую
//Т.к. размер разделяемой памяти равен размеру блока
//Каждая нить может скопировать по одному элементу
shared_vector[threadIdx.x] = V[threadIdx.x + THREAD_PER_BLOCK * k];
}
//Синхронизация. Нужно дождаться пока все нити скопируют свои элементы
//Потому что каждая нить использует скопированную часть вектора полностью
__syncthread();
//i - глобальный номер нити, N - количество строк в матрице
//Проверка не выходим ли за границы матрицы
if (i < N) {
//Умножение части вектора на часть строки
//j - порядковый номер элемента в части вектора или строки матрицы
//чтобы получить реальный номер элемента матрицы
//нужно прибавить j количеству полностью обработанных элементов
for(j = 0; j < THREAD_PER_BLOCK; ++j) {
C[i] += A[i][j + k * THREAD_PER_BLOCK] * shared_vector[j];
}
}
//Синхронизация нужна для того чтобы убедиться
//что все нити закончили работу с частью вектора
//потому что следующая операция - перезапись разделяемой памяти
__syncthread();
}
В данном коде можно сделать ещё одну оптимизацию – перенести операции над вектором C в разделяемую память. И скопировать результат в глобальную только после того как результат будет посчитан. С учётом этого алгоритм будет выглядеть следующим образом:
//Выделяем разделяемую память
//Количество элементов будет равно количеству нитей в блоке.
__shared__ shared_vector[THREAD_PER_BLOCK];
__shared__ shared_c[THREAD_PER_BLOCK];
shared_c[threadId.x] = 0;
__syncthread();
//k - номер части вектора. +1 потому что M не обязательно делится нацело
for(k = 0; k < (M / THREAD_PER_BLOCK) + 1; ++k) {
//p - реальный номер элемента вектора
//который будет скопирован нитью в разделяемую память
p = threadIdx.x + THREAD_PER_BLOCK * k;
//Проверка на выход за пределы вектора
if(p < M) {
//Копирование из глобальной памяти в разделяемую
//Т.к. размер разделяемой памяти равен размеру блока
//Каждая нить может скопировать по одному элементу
shared_vector[threadIdx.x] = V[threadIdx.x + THREAD_PER_BLOCK * k];
}
//Синхронизация. Нужно дождаться пока все нити скопируют свои элементы
//Потому что каждая нить использует скопированную часть вектора полностью
__syncthread();
//i - глобальный номер нити, N - количество строк в матрице
//Проверка не выходим ли за границы матрицы
if (i < N) {
//Умножение части вектора на часть строки
//j - порядковый номер элемента в части вектора или строки матрицы
//чтобы получить реальный номер элемента матрицы
//нужно прибавить j количеству полностью обработанных элементов
for(j = 0; j < THREAD_PER_BLOCK; ++j) {
shared_c[threadIdx.x] += A[i][j + k * THREAD_PER_BLOCK] * shared_vector[j];
}
}
//Синхронизация нужна для того чтобы убедиться
//что все нити закончили работу с частью вектора
//потому что следующая операция - перезапись разделяемой памяти
__syncthread();
}
if ( i < N ) {
С[i] = shared_c[threadId.x];
}
__syncthread();
Модификация кода хоста в этой лабораторной работе не требуется
Для анализа эффективности доступа к разделяемой памяти используйте утилиту nvprof. События для профилирования: shared_ld_bank_conflict – количество конфликтов банков памяти при считывании данных, shared_st_bank_conflict– количество конфликтов банков памяти при записи данных. И метрики: shared_efficiency – эффективность использования пропускной способности шины данных разделяемой памяти, shared_load_transactions_per_request – количество транзакций при каждом запросе к разделяемой памяти.
По аналогии оптимизируйте алгоритм
1. Прочитайте главу из теоретического материала "Разделяемая память" и ответьте на контрольные вопросы (ответы на контрольные вопросы не нужно включать в отчёт по лабораторной работе).
2. Оптимизируйте алгоритмы, реализованные в лабораторной работе №1 при помощи разделяемой памяти.
3. Постройте графики зависимости времени выполнения алгоритма от размера матрицы и вектора (Размеры матрицы 1000x500, 1000x1000, 1500x1000, 2000x1000, 2000x1500, 2500x1500, 2500x2000).
4. Проанализируйте, реализованные алгоритмы при помощи утилиты nvprof на эффективность доступа к глобальной памяти.
Методические указания по выполнению лабораторной работы
Разделяемая память – это своего рода кэш. Ускорения от использования разделяемой памяти можно достичь только если к каким-то данным происходит многократное обращение. Тогда переместив их в разделяемую память из глобальной можно сократить время затраченное на обращение в память за счёт высокой скорости разделяемой памяти.
Рассмотрим схему параллельного умножения матрицы на вектор.
Рис. 1 – схема умножения матрицы на вектор
Каждая нить берёт по одной строке матрицы и умножает попарно элементы строки на элементы вектора. Все нити используют один и тот же вектор. Скопировав вектор в разделяемую память можно получить ускорения выполнения алгоритма. Но проблема разделяемой памяти в том, что её объём очень мал по сравнению с объёмом глобальной памяти. Поэтому нужно предусмотреть ситуацию когда вектор полностью в память не помещается.
Учитывая, что разделяемой памяти может не хватить нужно модифицировать алгоритм таким образом чтобы часть вектора копировалась в разделяемую память. Нити умножали часть строки на часть вектора, расположенного в разделяемой памяти. Затем копировали следующую часть вектора в разделяемую память и умножали элементы следующей части строки и т.д. как показано на рисунке 2.
Рис. 2 – умножение нитью строки на вектор с копированием частей вектора в разделяемую память
Копирование частей вектора можно производить параллельно, каждая нить может копировать в разделяемую память один или несколько элементов. Таким образом псевдокод алгоритма будет выглядеть следующим образом:
//Выделяем разделяемую память
//Количество элементов будет равно количеству нитей в блоке.
__shared__ shared_vector[THREAD_PER_BLOCK];
//k - номер части вектора. +1 потому что M не обязательно делится нацело
for(k = 0; k < (M / THREAD_PER_BLOCK) + 1; ++k) {
//p - реальный номер элемента вектора
//который будет скопирован нитью в разделяемую память
p = threadIdx.x + THREAD_PER_BLOCK * k;
//Проверка на выход за пределы вектора
if(p < M) {
//Копирование из глобальной памяти в разделяемую
//Т.к. размер разделяемой памяти равен размеру блока
//Каждая нить может скопировать по одному элементу
shared_vector[threadIdx.x] = V[threadIdx.x + THREAD_PER_BLOCK * k];
}
//Синхронизация. Нужно дождаться пока все нити скопируют свои элементы
//Потому что каждая нить использует скопированную часть вектора полностью
__syncthread();
//i - глобальный номер нити, N - количество строк в матрице
//Проверка не выходим ли за границы матрицы
if (i < N) {
//Умножение части вектора на часть строки
//j - порядковый номер элемента в части вектора или строки матрицы
//чтобы получить реальный номер элемента матрицы
//нужно прибавить j количеству полностью обработанных элементов
for(j = 0; j < THREAD_PER_BLOCK; ++j) {
C[i] += A[i][j + k * THREAD_PER_BLOCK] * shared_vector[j];
}
}
//Синхронизация нужна для того чтобы убедиться
//что все нити закончили работу с частью вектора
//потому что следующая операция - перезапись разделяемой памяти
__syncthread();
}
В данном коде можно сделать ещё одну оптимизацию – перенести операции над вектором C в разделяемую память. И скопировать результат в глобальную только после того как результат будет посчитан. С учётом этого алгоритм будет выглядеть следующим образом:
//Выделяем разделяемую память
//Количество элементов будет равно количеству нитей в блоке.
__shared__ shared_vector[THREAD_PER_BLOCK];
__shared__ shared_c[THREAD_PER_BLOCK];
shared_c[threadId.x] = 0;
__syncthread();
//k - номер части вектора. +1 потому что M не обязательно делится нацело
for(k = 0; k < (M / THREAD_PER_BLOCK) + 1; ++k) {
//p - реальный номер элемента вектора
//который будет скопирован нитью в разделяемую память
p = threadIdx.x + THREAD_PER_BLOCK * k;
//Проверка на выход за пределы вектора
if(p < M) {
//Копирование из глобальной памяти в разделяемую
//Т.к. размер разделяемой памяти равен размеру блока
//Каждая нить может скопировать по одному элементу
shared_vector[threadIdx.x] = V[threadIdx.x + THREAD_PER_BLOCK * k];
}
//Синхронизация. Нужно дождаться пока все нити скопируют свои элементы
//Потому что каждая нить использует скопированную часть вектора полностью
__syncthread();
//i - глобальный номер нити, N - количество строк в матрице
//Проверка не выходим ли за границы матрицы
if (i < N) {
//Умножение части вектора на часть строки
//j - порядковый номер элемента в части вектора или строки матрицы
//чтобы получить реальный номер элемента матрицы
//нужно прибавить j количеству полностью обработанных элементов
for(j = 0; j < THREAD_PER_BLOCK; ++j) {
shared_c[threadIdx.x] += A[i][j + k * THREAD_PER_BLOCK] * shared_vector[j];
}
}
//Синхронизация нужна для того чтобы убедиться
//что все нити закончили работу с частью вектора
//потому что следующая операция - перезапись разделяемой памяти
__syncthread();
}
if ( i < N ) {
С[i] = shared_c[threadId.x];
}
__syncthread();
Модификация кода хоста в этой лабораторной работе не требуется
Для анализа эффективности доступа к разделяемой памяти используйте утилиту nvprof. События для профилирования: shared_ld_bank_conflict – количество конфликтов банков памяти при считывании данных, shared_st_bank_conflict– количество конфликтов банков памяти при записи данных. И метрики: shared_efficiency – эффективность использования пропускной способности шины данных разделяемой памяти, shared_load_transactions_per_request – количество транзакций при каждом запросе к разделяемой памяти.
По аналогии оптимизируйте алгоритм
Дополнительная информация
зачет
Похожие материалы
Программирование графических процессоров. Лабораторная работа №2
pleze
: 20 марта 2022
Тема: Работа с разделяемой памятью
Задание
1. Прочитайте главу из теоретического материала "Разделяемая память" и ответьте на контрольные вопросы (ответы на контрольные вопросы не нужно включать в отчёт по лабораторной работе).
2. Оптимизируйте алгоритмы, реализованные в лабораторной работе №1 при помощи разделяемой памяти.
3. Постройте графики зависимости времени выполнения алгоритма от размера матрицы и вектора (Размеры матрицы 1000x500, 1000x1000, 1500x1000, 2000x1000, 2000x1500, 2500x1500, 2
300 руб.
Лабораторная работа 2 Программирование графических процессоров Все варианты 2023 год
SibSUTTI
: 1 сентября 2023
2023 год
СибГУТИ
Сибирский государственный университет телекоммуникаций и информатики
Милешко Антон Владимирович
Тема: Лабораторная работа 1 2 3 Программирование графических процессоров Все варианты 2023 год
Задания
Лабораторная работа №2 по курсу «Программирование графических процессоров» на тему «Работа с разделяемой памятью»
Выполнение лабораторной работы поможет получить навыки требующиеся для выполнения второго и третьего заданий контрольной работы.
Задание
1. Прочитайте главу из теоретич
198 руб.
Лабораторная работа 1 2 3 Программирование графических процессоров Все варианты 2023 год
Alexey312451
: 16 марта 2024
Выполнение лабораторной работы поможет получить навыки, требующиеся для выполнения первого и третьего заданий контрольной работы.
Задание
1. Прочитайте главы теоретического материала под названиями "Отличия GPU от CPU", "Первая программа на CUDA C", "Алгоритм сложения двух векторов на GPU", "События, обработка ошибок и получение информации об устройстве", "Глобальная, локальная и константная память". Ответьте на контрольные вопросы и выполните контрольные задания, предложенные в конце этих глав
400 руб.
Лабораторная работа 1 2 3 Программирование графических процессоров Все варианты 2023 год
SibSUTTI
: 1 сентября 2023
2023 год
СибГУТИ
Сибирский государственный университет телекоммуникаций и информатики
Милешко Антон Владимирович
Тема: Лабораторная работа 1 2 3 Программирование графических процессоров Все варианты 2023 год
Задания
Лабораторная работа №1 по курсу «Программирование графических процессоров» на тему «Работа с глобальной памятью»
Выполнение лабораторной работы поможет получить навыки, требующиеся для выполнения первого и третьего заданий контрольной работы.
Задание
1. Прочитайте главы теоретическ
498 руб.
Лабораторная работа №2 "Работа с разделяемой памятью" по дисциплине "Программирование графических процессоров". Вариант общий
vpozyaikin
: 1 марта 2021
Выполнение лабораторной работы поможет получить навыки требующиеся для выполнения второго и третьего заданий контрольной работы.
Задание
1. Прочитайте главу из теоретического материала "Разделяемая память" и ответьте на контрольные вопросы (ответы на контрольные вопросы не нужно включать в отчёт по лабораторной работе).
2. Оптимизируйте алгоритмы, реализованные в лабораторной работе №1 при помощи разделяемой памяти.
3. Постройте графики зависимости времени выполнения алгоритма от размера матрицы
500 руб.
Лабораторная работа 2 (Вариант 3) По дисциплине: Программирование графических процессоров. Тема: «Работа с разделяемой памятью».
alexadubinina
: 21 ноября 2024
Задание
1. Прочитайте главу из теоретического материала "Разделяемая память" и ответьте на контрольные вопросы (ответы на контрольные вопросы не нужно включать в отчёт по лабораторной работе).
2. Оптимизируйте алгоритмы, реализованные в лабораторной работе №1 при помощи разделяемой памяти.
3. Постройте графики зависимости времени выполнения алгоритма от размера матрицы и вектора (Размеры матрицы 1000x500, 1000x1000, 1500x1000, 2000x1000, 2000x1500, 2500x1500, 2500x2000).
4. Проанализируйте, реал
300 руб.
Другие работы
Применение математических методов при обновлении парка автотранспортного предприятия
evelin
: 15 сентября 2013
Содержание
Введение
1. Расчет доходности постоянной ренты постнумерандо
2. Расчет эффективной ставки для контракта с Mercedes Benz. Расчет эффективной ставки для контракта с Лэйслер Холдинг Лимитед
3. Расчет приведенной стоимости потока платежей по договорам лизинга с
Mercedes Benz и Leisler Holding Limited
4. Технические характеристики Man TGA 18 480 BLS и Mercedes Benz1841 LS Actros
5. Расчет интегральных показателей
Список использованной литературы
Введени
Психология. Кейс 2. Вариант 1.
studypro3
: 24 июня 2019
Кейс-задание 2
Вариант 1
Ситуация 1
Проанализируйте ситуацию. Ученица 9-го класса полностью игнорирует один из предметов в школе. Умная, способная, независимая, она посещает уроки по этому предмету, но демонстративно ничего не делает, читает книги, журналы. Открыто выражает свое неуважение к учителю и откровенно говорит ему об этом.
Вопросы и задания:
Какие воспитательные меры, с вашей точки зрения, следует предпринять социальному педагогу?
Ситуация 2
Своеобразие структуры ценностных ориентац
400 руб.
Метод касательных (метод Ньютона)
Elfa254
: 29 сентября 2013
Используемая литература. 1
Метод Ньютона (касательных). 2
Описание. 2
Блок-схема алгоритма. 3
Листинг программы.. 4
Результаты работы программы.. 6
Пример №1. 6
Пример №2. 6
Пример №3. 7
Метод итераций. 8
Блок-схема алгоритма. 8
Листинг программы.. 9
Результаты работы программы.. 11
Пример №1. 11
Пример №2. 11
Пример №3. 12
Используемая литература
1. http://www.kyshtym.net.ru/rww/ Учимся программировать на С++
2. http://www.sprin.ru/soft.php Решение линейных уравнений методом
10 руб.
Гидравлика ИжГТУ 2007 Задача 2.3 Вариант 8
Z24
: 24 октября 2025
Жидкость вытесняется из сосуда сжатым газом и вытекает через трубу с сужением в атмосферу.
Найти абсолютное давление в суженном сечении d.
Учесть только путевые потери с коэффициентом сопротивления λ.
Плотность жидкости ρ=1000 кг/м³, ратм=1,013·105 Па.
200 руб.