Чего ждет мой графический процессор?

11

Я пишу программу 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, я ожидаю, что произойдет то же самое, но с работой, равномерно распределенной по двум очередям. Во всяком случае, я ожидаю, что каждая очередь будет иметь те же характеристики в отдельности, что и одна очередь: она начинает работать последовательно, пока все не будет сделано. Однако при использовании двух очередей я вижу, что не вся работа разбита на две аппаратные очереди:

введите описание изображения здесь

В начале работы графического процессора очередям удается запускать некоторые ядра асинхронно, хотя кажется, что ни одна из них не занимает полностью аппаратные очереди (если я не ошибаюсь). Ближе к концу работы с графическим процессором кажется, что очереди добавляют работу последовательно только в одну из аппаратных очередей, но бывают даже случаи, когда ядра не работают. Что дает? Есть ли у меня какое-то фундаментальное недопонимание того, как должна себя вести среда выполнения?

У меня есть несколько теорий относительно того, почему это происходит:

  1. Перемежающиеся clCreateBufferвызовы заставляют графический процессор синхронно распределять ресурсы устройства из общего пула памяти, что останавливает выполнение отдельных ядер.

  2. Базовая реализация OpenCL не отображает логические очереди в физические очереди, а только решает, куда поместить объекты во время выполнения.

  3. Поскольку я использую объекты GL, графический процессор должен синхронизировать доступ к специально выделенной памяти во время записи.

Являются ли какие-либо из этих предположений верными? Кто-нибудь знает, что может заставить GPU ждать в сценарии с двумя очередями? Любое понимание будет оценено!

Mokosha
источник
Я не могу найти, где написано, что в руководстве по программированию есть две очереди оборудования. Не могли бы вы опубликовать цитату из документа? Укажите, в какой главе написано, что есть две очереди? Можно ли запрашивать количество аппаратных очередей во время выполнения с использованием OpenCL?
Андреас
Я обновил свой пост. Это говорит о возможном исполнении, но если он может сделать несколько, почему он не может сделать их всех? Кроме того, среда выполнения OpenCL не имеет представления об аппаратной очереди, поэтому вы не можете запросить ее.
Мокоша

Ответы:

2

В общем случае вычисление очередей не обязательно означает, что теперь вы можете выполнять 2x отправки параллельно. Одна очередь, которая полностью насыщает вычислительные единицы, будет иметь лучшую пропускную способность. Многократные очереди полезны, если одна очередь потребляет меньше ресурсов (совместно используемая память или регистры), тогда вторичные очереди могут перекрываться на одном вычислительном устройстве.

Для рендеринга в реальном времени это особенно относится к таким вещам, как рендеринг теней, которые очень легки для вычислений / шейдеров, но тяжелы для аппаратных средств с фиксированными функциями, что освобождает планировщик GPU для выполнения асинхронной вторичной очереди.

Также нашел это в примечаниях к выпуску. Не знаю, если это та же проблема, но может быть, что CodeXL не велик. Я ожидал бы, что у этого, возможно, не было бы лучшего инструментария, для которого отправления находятся в полете.

https://developer.amd.com/wordpress/media/2013/02/AMD_CodeXL_Release_Notes.pdf

Для приложения, которое выполняет одновременную асинхронную передачу данных и выполнение ядра, временная шкала, показанная в представлении сеанса Application Trace, не будет показывать перекрытие этих операций. Это связано с тем, что драйвер и оборудование принудительно синхронизируют эти операции во время профилирования. (333981)

Кальвин
источник