Высокоуровневый интерфейс потокового программирования для системы ATI DPVM Адинец А.В., Сахарных Н.А. Научный руководитель Березин С.Б. Лаборатория Microsoft ВМК МГУ
Постановка задачи Удобный интерфейс для использования графических процессоров в параллельных вычислениях Что есть на данный момент? – Graphics API DirectX OpenGL – Высокоуровневые языки шейдеров HLSL GLSL Cg
Общие вычисления на GPU Линейная алгебра Численные методы Обработка изображений Физическая симуляция
ATI DPVM
Компоненты ATI DPVM ATI Close-to-the-Metal (CTM) – возможность чтения/записи в буфер в одном шейдере – чтение/запись непосредственно в память GPU – быстрые (tiled) форматы данных fetch4 (получение 4-x float за один тик) ATI AMU compiler – компиляция шейдера на ps_3_0 в бинарную программу для DPVM
Потоковое программирование Массив данных A Массив данных B Массив данных C Ядро – программа для обработки массивов данных Out = A + B * C Массив данных Out
DPVM Wrapper Высокоуровневый интерфейс для управления памятью ATI DPVM – выделение/освобождение блоков памяти Работа с буфером команд в терминах потокового программирования – ядра, которые состоят из шейдера и списка формальных параметров – работа в режиме накопления (retained) Интерфейс для работы с буферами данных и константными буферами Различные способы оптимизации – маскирование – отсечение по домену Получение статистики (performance counters)
Режимы работы Immediate – непосредственное создание буфера команд и выполнение при каждом вызове Retained – данные накапливаются в некотором списке – при попытке прочитать/записать в буфер, формируется буфер команд и подается на выполнение
Пример программы Размер массива (домен) ivec2 wsize = new ivec2(512, 512); Шейдер (ps_3_0) string shaderStr = "ps_3_0" + "dcl vPos.xy" + "dcl_2d s0" + "texld r0, vPos, s0" +// r0 = input "mul oC0, r0, c0";// output = r0 * c0 Описание параметров ядра string argStr = "vec4 c0 c0, " + "dataf in s0, " + "dataf out oC0";
Пример программы (2) // создаем устройство ManagedDevice dev = new ManagedDevice("sample"); // создаем 2 буфера DataBuffer inb = new DataBuffer (dev, wsize); DataBuffer outb = new DataBuffer (dev, wsize); vec4[,] input = new vec4[wsize.x, wsize.y]; //..заполняем массив input данными.. inb.writeValues(input); // устанавливаем константу vec4 cval = new vec4( 0.5f, 0.5f, 0.5f, 0.5f );
Пример программы (3) // создаем ядро SingleDppKernel p = new SingleDppKernel( dev, shaderStr, SourceType.ps_3_0, argStr, false ); // вызываем ядро с указанными параметрами p.invoke(cval, inb, outb); // читаем выходной массив данных vec4[,] output = outb.readValues();
Результаты Простой и удобный интерфейс для разработки небольших программ для графических процессоров – объем кода сравнительно меньше аналогичной программы для сырого DPVM – производительность: перемножение матриц 26 Gflops (пик 42) – ленивые вычисления на GPU
Что дальше? Язык программирования C$ – back-end Использование в других проектах в качестве оболочки над DPVM
Вопросы?