210 likes | 387 Views
Автоматизация разработки приложений для графических ускорителей с использованием переписывающих правил. Анатолий Дорошенко, Константин Жереб Институт программных систем НАН Украины Киевское отделение МФТИ. Почему графические ускорители.
E N D
Автоматизация разработки приложений для графических ускорителей с использованием переписывающих правил Анатолий Дорошенко, Константин Жереб Институт программных систем НАН Украины Киевское отделение МФТИ
Почему графические ускорители • Специализированные устройства, доступные на большинстве компьютеров • Изначально разработаны для решения графических задач • Рыночные требования привели к значительному повышению производительности • Можно использовать для общецелевых вычислений (General Purpose computation on Graphical Processing Units, GPGPU)
Архитектура GPU • 16 мульти-процессоров • 128 ядер • 8192 регистра • 16KБ разделяемой памяти
Программирование GPU • Использование графических средств (шейдеры) • Специализированные средства GPGPU • NVidia CUDA • AMD Stream • OpenCL
Модель программирования CUDA • Вычислительные ядра (kernel) • Каждое ядро выполняется на многих потоках (thread) • Потоки объединены в блоки (block), которые объединены в решетки (grid)
Взаимодействие с CPU • Язык С – два способа вызова • C for CUDA – расширение языка С • Driver API – набор функций (сложнее, но предоставляет больший контроль) • C#, Java • Библиотеки от сторонних разработчиков (CUDA.NET, jCUDA) • Основаны на driver API (сложность вызова) • Ядра нужно все равно писать на C for CUDA
Почему автоматизация • Сложная модель программирования • Сложная иерархия памяти • Эффективные программы требуют низкоуровневой оптимизации • Сложное взаимодействие с языками высокого уровня
Переписывающие правила • Программа представляется в виде термов t=f(t1, ...,tn) • Использование переписывающих правил source [condition] destination [action] • source – исходный терм (образец) • condition – условие применения (необязательно) • destination– преобразованный терм • action – дополнительное действие(необязательно)
Платформа Termware • Платформа переписывающих правил • Декларативное описание • Варианты использования • Самостоятельно (командная строка, графический интерфейс) • Интегрируется в приложения • Изначально на Java, портирована на C#
Почему переписывающие правила • Способ описания преобразований программ • Декларативное описание • В сложных случаях можно использовать процедурный код
Что автоматизируем • Переход к использованию CUDA • Входные данные – программа на C# • Оптимизация для CUDA • Структура циклов • Доступ к памяти • Пример – игра «Жизнь»
Переход от C# к С for CUDA Скомпилированное ядро .cubin Cfor CUDA Ядро CUDA Интенсивные вычисления C# CUDA .NET Исходная программа Вызов ядра C# Преобразованная программа
Пример правилГенерация ядра CUDA • _CudaKernel2D($name, $params, $body, $idx1,$idx2) -> _CudaFunctionKernel($name, $params, [_PrepareIdx($idx1,x),_PrepareIdx($idx2,y), _CsToCuda($body)]) • _CudaFunctionKernel($name, $params, $body) -> Function([ExternC,__global__], void,$name,$params,$body) • _PrepareIdx($idx,$coor) -> DeclarationAssignment( $idx,int, _CudaIdx($coor)) • _CudaIdx($coor) -> Dot(blockIdx,$coor) * Dot(blockDim,$coor)+ Dot(threadIdx,$coor)
Вызов ядра • Проверка наличия CUDA • Инициализация CUDA • Инициализация ядра • Копирование входных данных в графическую память • Передача параметров ядра • Вызов ядра • Копирование результатов • Освобождение графической памяти
Сгенерированный код // Init CUDA, select 1st device. CUDAcuda = newCUDA(0, true); // load kernel string path = Path.Combine(Environment.CurrentDirectory, "life_kernel.cubin"); cuda.LoadModule(path); CUfunctionfunc = cuda.GetModuleFunction("life_kernel"); //... // allocate memory and copy to device CUdeviceptrdev_input = cuda.CopyHostToDevice<int>(input); CUdeviceptrdev_output = cuda.CopyHostToDevice<int>(output); //... //pass parameters cuda.SetParameter(func, 0, (uint)dev_input.Pointer); cuda.SetParameter(func, IntPtr.Size, (uint)dev_output.Pointer); cuda.SetParameterSize(func, (uint)(2*IntPtr.Size)); //call kernel cuda.SetFunctionBlockShape(func, BLOCK_SIZE, BLOCK_SIZE, 1); cuda.Launch(func, FIELD_SIZE / BLOCK_SIZE, FIELD_SIZE / BLOCK_SIZE); //... //copy from device and release memory cuda.CopyDeviceToHost<int>(dev_output, output); cuda.Free(dev_input); cuda.Free(dev_output);
ОптимизацияРазделяемая (shared) память • Добавляем буферы (свой для каждого блока) • Каждый поток копирует свою часть данных в буфер • Все вычисления – в буферах, без доступа в глобальную память • Результаты копируются в глобальную память
Повышение производительности • Поле 512x512, 1000 итераций • Без оптимизаций ускорение в 8 раз • Практически не требовало вмешательства разработчика • Оптимизации позволили достичь 25-кратного ускорения
Выводы • GPU – новая перспективная платформа • Программирование для GPU сейчас сложно и требует автоматизации • Автоматизация преобразований – переписывающие правила • Распараллеливание и оптимизация программ для GPU • Достигнуто ускорение в 25 раз • Дальнейшие исследования • Новые преобразования • Новые задачи • Поддержка других технологий
Спасибо за внимание! Вопросы? doroshenko@intspei.com zhereb@gmail.com