Лихогруд Николай n.lihogrud@gmail.com Задание. Свертка с квадратным ядром tmp = 0; for(ik=-r..r) for(jk=-r..r) tmp += matrix[i+ik][j+jk]*filter[ik+r][jk+r];

Презентация:



Advertisements
Похожие презентации
Лихогруд Николай Задание. Постановка.
Advertisements

Разработка на CUDA с использованием Thrust Михаил Смирнов.
Двумерные динамические массивы. Двумерный массив - это одномерный массив, элементами которого являются одномерные массивы. Другими словами, это набор.
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Половинкин А.Н.. Постановка задачи Алгоритм умножения матриц на GPU Программная реализация.
Функции Лекция 8. Назначение функций Функции - самостоятельные программные единицы, спроектированные для решения конкретной задачи. Функции по структуре.
ПРОЦЕДУРЫ И ФУНКЦИИ CPascal Подпрограмма – группа операторов реализующая законченный алгоритм и оформленная как самостоятельная синтаксическая единица.
ООП Классы – 2.
Половинкин А.Н.. Постановка задачи Алгоритм вычисления функции axpy на GPU Программная реализация.
Основы информатики Лекция. Функции Заикин Олег Сергеевич
Лихогруд Николай Часть седьмая.
Задания части А Задания части С. 1. Значения двух массивов A[1..100] и B[1..100] задаются с помощью следующего фрагмента программы. Сколько элементов.
МАССИВЫ 4 Определение 4 Описание 4 Обращение к элементам массива 4 Связь массивов с указателями 4 Примеры программ.
Лекция 6 Функции. Объявления и определения Объявление функции – указание имени функции, а также входных и выходных параметров Определение функции – указание.
С++, начала ООП Семинар 1 Рябова Анна Сергеевна
Инструкции C++ Условная инструкция Формат: if (условие) оператор; else оператор; Пример: if (i!=0) { if (j) j++; if(k) k++; else if(p) k--; } else i--;
Лекция 21. Шаблоны (часть 1) Красс Александр СПбГУ ИТМО, 2008.
Функции Функция – именованная последовательность описаний и операторов, выполняющая некоторое действие. Может иметь параметры и возвращать значение. Функция.
Массивы 9 класс. Основные теоретические сведения Примеры решения задач.
Д.з Язык С++ - занятие 31. Задача 1: 1/1 + 1/3 + 1/5 … #include using namespace std; int main() { int n; cin >> n; double sum = 0;// Сумма for.
Транксрипт:

Лихогруд Николай Задание

Свертка с квадратным ядром tmp = 0; for(ik=-r..r) for(jk=-r..r) tmp += matrix[i+ik][j+jk]*filter[ik+r][jk+r]; result[i][j]=tmp ; Квадратное ядро

Фильтрация изображений

Базовое задание Выбрать любые два фильтра из статьи Разобраться с какой-либо библиотекой для работы с изображениям (предлагаю DevIL ) Реализовать загрузку, модификацию и сохранение изображений в формате png/jpeg с палитрой RGBA Реализовать добавление к изображению рамки с шириной, равной радиусу фильтра Рамка заполняется крайними пикселами изображения

Базовое задание Написать ядро, принимающее на вход Фильтр (через константную память) Исходную матрицу с рамкой Память под матрицу-результат (тоже с рамкой) Каждая нить вычисляет один элемент матрицы-результата Ядро фильтрации передается через константную память #define MAX_KERNEL_WIDTH (2 * MAX_KERNEL_RADIUS + 1) __constant__ float kernel[MAX_KERNEL_WIDTH * MAX_KERNEL_WIDTH] __global__ void filter(cudaPitchedPtr inMatrix, cudaPitchedPtr outMatrix, int kernelRadius) {}

Работа с пикселями Ядро работает с массивами пикселов, каждая нить рассчитывает пиксел выходного изображения – компоненты R, G, B Удобно использовать класс Pixel с полями uint8_t r,g,b,a ; и FloatPixel с полями float r,g,b,a Через Pixel читаем/пишем память В FloatPixel накапливаем результат Для считывания/записи используем get_pixel – аналог get_elem, но приводит указатель на начало строки к типу Pixel, а не float

Pixel #include struct Pixel { uint8_t r,g,b,a; __host__ __device__ Pixel &operator=(const Pixel &otherPixel) { *((uint32_t *)this) = (*(uint32_t *)&otherPixel); return *this; } __host__ __device__ Pixel(const Pixel &otherPixel) { *((uint32_t *)this) = (*(uint32_t *)&otherPixel); } __host__ __device__ Pixel(uint8_t r, uint8_t g, uint8_t b, uint8_t a); __host__ __device__ FloatPixel operator*(float coefficient) }; Оператор присваивания и конструктор копирования копирует сразу 4 байта, а не побайтовое

Замечания к классу Pixel __host__ __device__ означают, что функция будет скомпилирована и для CPU, и для GPU При работе с памятью важно считывать/записывать пиксел одной 4- байтной инструкцией, вместо 4 однобайтовых С этой целью переопределены конструктор копирования и оператор присваивания Pixel *pixels; // массив пикселов Pixel pixel2 = pixels[2]; // Конструктор копирования Pixels[0] = pixel1 + pixel2; // Оператор присваивания Проверьте через $ cuobjdump -sass, чтобы в ассемблере не было инструкций ST/LD c суффиксом U8

FloatPixel #include struct FloatPixel { float r,g,b,a; __host__ __device__ FloatPixel(float r, float g, float b, float a); __host__ __device__ FloatPixel operator+(const FloatPixel &otherPixel); __host__ __device__ Pixel getPixel(); }; Оператор сложения с другим FloatPixel для накопления результата

Про эрозию и наращивание В эрозии и наращивании нужно найти в окрестности элемент с максимальной/минимальной яркостью Окрестность задается матрицей из нулей и единиц Яркость пиксела можно записать в четвертую компоненту, все равно мы её не используем Тогда можно добавить оператор Pixel &operator>(Pixel &otherPixel), возвращающий пиксел с большей четвертой компонентой Яркость всех пикселов нужно посчитать один раз, на хосте или GPU, по формуле 0.3*R *G *B (или 0.21R G B)

Синтетические данные Для экспериментов с большими матрицами: Добавить возможность генерации случайных матриц из чисел (не пикселов) Добавить отдельное ядро, работающее с матрицами из вещественных чисел Каждая нить считает не пиксел а просто сумму произведений элементов окрестности на коэффициенты ядра

Требования к базовой части Входные параметры: Радиус фильтра Размеры синтетической матрицы или файл с входным изображением Программа: Выводися время применения фильтров Если на входе было изображение – сохраняет на диск результат применения двух фильтров

Добавляем потоки Программа дополнительно принимает число потоков Разрежем матрицу на горизонтальные полосы по числу потоков Каждый поток рассчитывает одну из полос

Добавляем потоки Для старта вычислений в нулевом потоке достаточно скопировать первую полосу вместе с её нижней рамкой

Добавляем потоки Для старта вычислений в следующем потоке часть данных уже скопирована Что уже есть на GPU

Добавляем потоки Для следующего потока так же часть данных уже будет на GPU Что уже есть на GPU

Ускорение При помощи профилировщика подобрать радиус фильтра и размеры синтетической матрицы так, чтобы копирования совпадали со временем выполнения Максимальное ускорение

Используем multi-GPU Разрезать матрицу на горизонтальные полосы по числу видеокарт Разослать по видеокартам полосы + их рамки Каждая видеокарта считает независимо свою часть С использованием потоков Использовать OpenMP

3 GPU

Второй фильтр & multi GPU Для применения второго фильтра понадобятся рамки полос, вычисленные на соседних устройствах Их нужно переслать после применения первого фильтра через cudaMemCpyAsync ()/ cudaMemcpyPeerAsync () Включить прямой доступ (peerAccess) там, где это возможно

end