Я разрабатываю приложение OpenCL 1.2, которое имеет дело с большими изображениями. На данный момент изображение, которое я тестирую, составляет 16507x21244 пикселей. Ядро запускается в цикле, который работает с фрагментами изображения. Ядро занимает 32bpp (rgba) куски изображения в и пропускает float4-пиксельные фрагменты.
Определите одну сторону (квадрата) куска в пикселях, чтобы быть размером куска. То есть размер 8192x8192 пикселей имеет размер 8192. Конечно, с правой и нижней стороны у нас есть меньшие прямоугольные куски, если изображение не чисто делится на размер куска. Мой код обрабатывает это, но для остальной части этого сообщения мы будем игнорировать это для простоты.
Я стараюсь определить максимальный размер куска, который я могу использовать на каждой итерации моего цикла, а также оптимальный размер блока (который может быть не максимальным размером блока).
Для справки вот информация, указанная утилитой clinfo на моей машине. Я запускаю свое ядро на Geforce GTX 560 Ti
с платформой Nvidia, используя их собственные Linux-драйверы.
Мое первоначальное наивное предположение заключалось в том, что я мог работать с максимальным размером изображения 2d.
Однако это приводит к тому, что clEnqueueNDRangeKernel
возвращает код ошибки -4 (CL_MEM_OBJECT_ALLOCATION_FAILURE
).
Размышляя об этом, это имеет смысл для меня. С 1 Гбайт видеопамяти можно было бы ожидать, чтобы иметь возможность содержать одну текстуру 16384x16384 пикселей (32bpp) или текстуру 8192x8192 пикселей (float4). Если оба они должны быть кэшированы на карточке во время работы ядра, мы можем рассчитывать на использование следующего объема памяти:
4 bytes-per-pixel * chunk size^2 (input image)
+ 16 bytes-per-pixel * chunk size^2 (output image)
= 1 GiB total video memory
Решение для размера блока мы получаем
chunk size = sqrt(1GiB/20)
Включение объема памяти, сообщенного OpenCL (который немного меньше, чем 1GiB - 1023 MiB) и настил результата, получаем:
floor(sqrt(1072889856/20)) = 7324
Однако размер куска 7324 по-прежнему приводит к CL_MEM_OBJECT_ALLOCATION_FAILURE
.
Мое следующее предположение состояло в том, что мы не можем передать изображение, большее размера max, которое OpenCL сообщает как 268222464 байт для моей карты. Поскольку мое выходное изображение имеет большую ширину пикселя, оно будет определять размер моего блока.
floor(sqrt(268222464/16)) = 4094
Эй, это действительно работает! Теперь, что, если мы попытаемся пойти больше? К моему удивлению, это не подводит. Сквозь проб и ошибок я сузился на 6784 как фактический максимальный размер куска. В 6785 году он начинает жаловаться на CL_MEM_OBJECT_ALLOCATION_FAILURE
. Я не знаю, почему max составляет 6784, и я не знаю, является ли это повторяемым или если значение колеблется (например, другое состояние, существующее в видеопамяти, влияющее на то, сколько он может удерживать.) Я также считаю, что работа с размер куска 6784 на несколько секунд медленнее, чем работа с размером, основанным на максимальном распределении. Интересно, это потому, что OpenCL необходимо выполнить несколько (дорогостоящих) распределений под капотом? Я также заметил "максимальный размер аргумента ядра", который OpenCL может сообщить (CL_DEVICE_MAX_PARAMETER_SIZE
). Однако эта ценность кажется фиктивной. Если бы я мог пропускать только 4096 байт, это ограничило бы меня 16x16 пикселей!
Итак, у меня остались два фундаментальных вопроса:
- Как определить абсолютный максимальный размер блока?
- Как определить самый быстрый размер куска? (Есть ли метод, отличный от проб и ошибок?)
В качестве бонусного вопроса есть ли хорошие ресурсы, на которые я мог бы обратиться в будущих вопросах такого характера относительно низкоуровневых OpenCL-аппаратных взаимодействий?
И, наконец, я дам несколько фрагментов кода для экспертной оценки; Я был бы чрезвычайно благодарен за любую конструктивную критику!
Как всегда, заранее за помощь!