Я пишу программу OpenCL для использования с моим графическим процессором AMD Radeon HD 7800 серии. Согласно руководству по программированию AMD OpenCL , это поколение GPU имеет две аппаратные очереди, которые могут работать асинхронно.
5.5.6 Очередь команд
Для Южных островов и более поздних версий устройства поддерживают как минимум две очереди аппаратных вычислений. Это позволяет приложению увеличить пропускную способность небольших диспетчеров с двумя очередями команд для асинхронной отправки и, возможно, выполнения. Очереди аппаратных вычислений выбираются в следующем порядке: первая очередь = четные очереди команд OCL, вторая очередь = нечетные очереди OCL.
Для этого я создал две отдельные очереди команд OpenCL для подачи данных в графический процессор. Грубо говоря, программа, запущенная на хост-потоке, выглядит примерно так:
static const int kNumQueues = 2;
cl_command_queue default_queue;
cl_command_queue work_queue[kNumQueues];
static const int N = 256;
cl_mem gl_buffers[N];
cl_event finish_events[N];
clEnqueueAcquireGLObjects(default_queue, gl_buffers, N);
int queue_idx = 0;
for (int i = 0; i < N; ++i) {
cl_command_queue queue = work_queue[queue_idx];
cl_mem src = clCreateBuffer(CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, ...);
// Enqueue a few kernels
cl_mem tmp1 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel1, queue, src, tmp1);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp1);
cl_mem tmp2 = clCreateBuffer(CL_READ_WRITE);
clEnqueueNDRangeKernel(kernel2, queue, tmp1, tmp2);
clEnqueueNDRangeKernel(kernel3, queue, tmp2, gl_buffer[i], finish_events + i);
queue_idx = (queue_idx + 1) % kNumQueues;
}
clEnqueueReleaseGLObjects(default_queue, gl_buffers, N);
clWaitForEvents(N, finish_events);
При kNumQueues = 1
этом это приложение работает в основном так, как задумано: оно собирает всю работу в одну очередь команд, которая затем выполняется до конца, и GPU все время довольно занят. Я могу увидеть это, посмотрев на вывод профилировщика CodeXL:
Однако, когда я установлю kNumQueues = 2
, я ожидаю, что произойдет то же самое, но с работой, равномерно распределенной по двум очередям. Во всяком случае, я ожидаю, что каждая очередь будет иметь те же характеристики в отдельности, что и одна очередь: она начинает работать последовательно, пока все не будет сделано. Однако при использовании двух очередей я вижу, что не вся работа разбита на две аппаратные очереди:
В начале работы графического процессора очередям удается запускать некоторые ядра асинхронно, хотя кажется, что ни одна из них не занимает полностью аппаратные очереди (если я не ошибаюсь). Ближе к концу работы с графическим процессором кажется, что очереди добавляют работу последовательно только в одну из аппаратных очередей, но бывают даже случаи, когда ядра не работают. Что дает? Есть ли у меня какое-то фундаментальное недопонимание того, как должна себя вести среда выполнения?
У меня есть несколько теорий относительно того, почему это происходит:
Перемежающиеся
clCreateBuffer
вызовы заставляют графический процессор синхронно распределять ресурсы устройства из общего пула памяти, что останавливает выполнение отдельных ядер.Базовая реализация OpenCL не отображает логические очереди в физические очереди, а только решает, куда поместить объекты во время выполнения.
Поскольку я использую объекты GL, графический процессор должен синхронизировать доступ к специально выделенной памяти во время записи.
Являются ли какие-либо из этих предположений верными? Кто-нибудь знает, что может заставить GPU ждать в сценарии с двумя очередями? Любое понимание будет оценено!
Ответы:
В общем случае вычисление очередей не обязательно означает, что теперь вы можете выполнять 2x отправки параллельно. Одна очередь, которая полностью насыщает вычислительные единицы, будет иметь лучшую пропускную способность. Многократные очереди полезны, если одна очередь потребляет меньше ресурсов (совместно используемая память или регистры), тогда вторичные очереди могут перекрываться на одном вычислительном устройстве.
Для рендеринга в реальном времени это особенно относится к таким вещам, как рендеринг теней, которые очень легки для вычислений / шейдеров, но тяжелы для аппаратных средств с фиксированными функциями, что освобождает планировщик GPU для выполнения асинхронной вторичной очереди.
Также нашел это в примечаниях к выпуску. Не знаю, если это та же проблема, но может быть, что CodeXL не велик. Я ожидал бы, что у этого, возможно, не было бы лучшего инструментария, для которого отправления находятся в полете.
https://developer.amd.com/wordpress/media/2013/02/AMD_CodeXL_Release_Notes.pdf
источник