Вычисление фрактала Джулиа на GPU

Опубликовано апреля 27, 2018 в Технология CUDA

В полном соответствии с отмеченной ранее тенденцией реализация для графического процессора очень похожа на реализацию для CPU.

Эта версия main () выглядит намного сложнее версии для CPU, но общая структура точно такая же. Как и в версии для CPU, мы создаем растровое изображение размером DIM х DIM с помощью библиотечной функции. Но поскольку вычисления будут производиться на GPU, то мы еще объявляем указатель dev_bitmap на область памяти устройства, где будут храниться данные. А чтобы выделить эту область, нужно вызвать функцию cudaMalloc ().

Далее мы запускаем функцию kernel () точно так же, как в версии для CPU,хотя теперь она помечена квалификатором _global_ и, значит, выполняется на GPU. Как и раньше, мы передаем функции kernel () полученный в предыдущей строке указатель на область для сохранения результата. Разница лишь   в том, что память теперь принадлежит графическому процессору, а не CPU.

Вычисление фрактала Джулиа на GPU

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

dim3 — это не стандартный тип С, не пугайтесь, что с вами случился провал в памяти. Просто в заголовочных файлах исполняющей среды CUDA определены типы, инкапсулирующие многомерные кортежи.

3D модели: вазы, кадки, цветочные горшки! Можно скачать на сайте 3d-modeli.net, как отдельные модели ваз так и наборы декоративных вазочек и подобных предметов для домашнего дизайна!

Если честно, то потому, что исполняющая среда CUDA ожидает получить значение типа dim3. И хотя трехмерные сетки в настоящее время не поддерживаются, CUDA все равно хочет видеть значение типа dim3, в котором последняя компонента равна 1. Если мы при инициализации указываем только две компоненты, как в предложении dim3 grid (0IM, DIM), то исполняющая среда CUDA автоматически подставит вместо третьей значение 1, поэтому все будет работать правильно. Вполне возможно, что в будущем NVIDIA станет поддерживать и трехмерные сетки, но пока мы должны придерживаться API запуска ядра, потому что в борьбе программиста с API всегда побеждает API.

И наконец, так как результаты, сформированные в результате выполнения kernel (), находятся в памяти устройства, мы должны скопировать их в память CPU. Мы уже знаем, что для этого следует вызвать функцию cudaMemcpy (), передав в последнем аргументе константу cudaMemcpyDeviceToHost.

И, в отличие от версии для CPU, нам больше не нужны вложенные циклы for для порождения индексов пикселей, передаваемых функции julia (). Как и в задаче о сложении векторов, исполняющая среда CUDA предоставляет нам эти индексы в переменной blockldx. Это работает, потому что сетка блоков имеет столько же размерностей, сколько изображение, гак что мы получаем по одному блоку для каждой пары целых чисел (х, у) в диапазоне от (0,0) до (DIM-1, DIM-1).

Единственное, что нам еще нужно, — это линейное смещение от начала буфера ptr. Оно вычисляется с помощью еще одной встроенной переменной, gridDim. Эта переменная одинакова во всех блоках и содержит размерности запущенной сетки. В нашем случае она будет содержать значение (DIM, DIM). Поэтому, чтобы получить смещение от начала буфера в диапазоне от 0 до (DIM х DIM — 1), мы умножаем индекс строки на ее ширину и прибавляем индекс столбца: