3 очереди + 1 финиш или контрольная точка на стороне устройства для всех очередей

Существует ли специальная функция «ожидания события», которая может одновременно ожидать 3 очереди на стороне устройства, чтобы она не ждала все очереди последовательно со стороны хоста?

Существует ли команда контрольной точки для отправки в очередь команд, так что она должна ждать, пока другие очереди команд не достигнут того же (вертикального) барьера/контрольной точки, чтобы ждать и продолжать работу со стороны устройства, поэтому не требуется обходной путь на стороне хоста?

На данный момент я попробовал две разные версии:

clWaitForEvents(3, evt_);

а также

int evtStatus0 = 0;
clGetEventInfo(evt_[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
    sizeof(cl_int), &evtStatus0, NULL);

while (evtStatus0 > 0)
{

    clGetEventInfo(evt_[0], CL_EVENT_COMMAND_EXECUTION_STATUS,
        sizeof(cl_int), &evtStatus0, NULL);
    Sleep(0);
}

int evtStatus1 = 0;
clGetEventInfo(evt_[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
    sizeof(cl_int), &evtStatus1, NULL);

while (evtStatus1 > 0)
{

    clGetEventInfo(evt_[1], CL_EVENT_COMMAND_EXECUTION_STATUS,
        sizeof(cl_int), &evtStatus1, NULL);
    Sleep(0);
}


int evtStatus2 = 0;
clGetEventInfo(evt_[2], CL_EVENT_COMMAND_EXECUTION_STATUS,
    sizeof(cl_int), &evtStatus2, NULL);

while (evtStatus2 > 0)
{

    clGetEventInfo(evt_[2], CL_EVENT_COMMAND_EXECUTION_STATUS,
        sizeof(cl_int), &evtStatus2, NULL);

    Sleep(0);
}

второй немного быстрее (я видел это у кого-то другого), и оба выполняются после 3 команд сброса.

Глядя на результаты профилировщика CodeXL, первый из них дольше ждет между точками финиша, а некоторые операции даже не кажутся перекрывающимися. Второй показывает, что 3 точки завершения находятся в пределах 3 миллисекунд, поэтому он быстрее, и более длинные части перекрываются (чтение + запись + вычисление одновременно).

Если есть способ добиться этого с помощью только одной команды ожидания со стороны хоста, должна быть и ее версия «flush», но я не смог найти.

Есть ли способ получить изображение ниже вместо добавления сбросов между каждым этапом конвейера?

queue1 write checkpoint write    checkpoint write
queue2  -               compute  checkpoint compute checkpoint compute
queue3  -                        checkpoint read    checkpoint read     

все контрольные точки должны быть вертикально синхронизированы, и все эти действия не должны начинаться, пока не будет дан сигнал. Такие как:

queue1.ndwrite(...);
queue1.ndcheckpoint(...);
queue1.ndwrite(...);
queue1.ndcheckpoint(...);
queue1.ndwrite(...);
queue2.ndrangekernel(...);
queue2.ndcheckpoint(...);
queue2.ndrangekernel(...);
queue2.ndcheckpoint(...);
queue2.ndrangekernel(...);
queue3.ndread(...);
queue3.ndcheckpoint(...);
queue3.ndread(...);
queue3.ndcheckpoint(...);
queue3.ndread(...);

queue1.flush() 
queue2.flush()
queue3.flush()

queue1.finish()
queue2.finish()
queue3.finish()

все контрольные точки обрабатываются на стороне устройства, и со стороны хоста требуется только 3 команды завершения (даже лучше, только 1 завершение для всех очередей?)

Как привязать 3 очереди к 3 событиям с помощью "clWaitForEvents(3, evt_);" на данный момент это:

hCommandQueue->commandQueue.enqueueBarrierWithWaitList(NULL, &evt[0]);
hCommandQueue2->commandQueue.enqueueBarrierWithWaitList(NULL, &evt[1]);
hCommandQueue3->commandQueue.enqueueBarrierWithWaitList(NULL, &evt[2]);

если этот «барьер очереди» может общаться с другими очередями, как я могу этого добиться? Нужно ли поддерживать события на стороне хоста, пока все очереди не будут завершены, или я могу удалить их или повторно использовать позже? Из документации кажется, что событие первого барьера может быть помещено во вторую очередь, а событие второго барьера может быть помещено в третье вместе с событием первого, поэтому, возможно, это похоже на:

hCommandQueue->commandQueue.enqueueBarrierWithWaitList(NULL, &evt[0]);
hCommandQueue2->commandQueue.enqueueBarrierWithWaitList(evt_0, &evt[1]);
hCommandQueue3->commandQueue.enqueueBarrierWithWaitList(evt_0_and_1, &evt[2]);

in the end wait for only evt[2] maybe or using only 1 same event for all:

hCommandQueue->commandQueue.enqueueBarrierWithWaitList(sameEvt, &evt[0]);
hCommandQueue2->commandQueue.enqueueBarrierWithWaitList(sameEvt, &evt[1]);
hCommandQueue3->commandQueue.enqueueBarrierWithWaitList(sameEvt, &evt[2]);

where to get sameEvt object?

кто-нибудь пробовал это? Должен ли я запускать все очереди с барьером, чтобы они не запускались до тех пор, пока я не вызову какое-либо событие со стороны хоста, или ленивые выполнения «enqueue» на 100% можно доверять «не запускать, пока я не сброслю / не закончу» их? Как передать событие от хоста к устройству (у того же самогоEvt нет функции «повышения», это clCreateUserEvent?)?

Все 3 очереди расположены по порядку и находятся в одном контексте. Нестандартный тип поддерживается не всеми видеокартами. Используются привязки C++.

Также есть enqueueWaitList (это устарело?) и clEnqueueMarker, но я не знаю, как их использовать, и в документации нет примера на веб-сайте Khronos.


person huseyin tugrul buyukisik    schedule 08.09.2016    source источник


Ответы (2)


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

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

queue1.enqueue(comm1, /*dependency*/ NULL, /*result event*/ &e1);
queue2.enqueue(comm2, /*dependency*/ &e1, /*result event*/ NULL);

В этом примере comm2 будет ждать завершения comm1.

Если вам нужно сначала поставить команды в очередь, но не разрешить их выполнение, вы можете создать пользовательское событие (clCreateUserEvent) и сигнализировать об этом вручную (clSetUserEventStatus). Реализации разрешено обрабатывать команды, как только они поставлены в очередь (драйверу не требуется ждать flush).

Барьер кажется излишним для вашей цели, потому что он ожидает всех команд, ранее отправленных в очередь. Вы действительно можете использовать clEnqueueMarker, который можно использовать для ожидания всех событий и предоставления одного события для использования другими командами.

Насколько я знаю, вы можете сохранить событие в любой момент, если оно вам больше не нужно. Реализация должна продлевать время жизни события, если это требуется для внутренних целей.

Я не знаю, что такое enqueueWaitList.

Не по теме: если вам нужны нетривиальные зависимости между вычислениями, вы можете рассмотреть TBB блок-схема и opencl_node. opencl_node использует события для синхронизации и по возможности избегает синхронизации "хост-устройство". Однако может быть сложно использовать несколько очередей для одного и того же устройства.

Насколько мне известно, Intel HD Graphics 530 поддерживает неупорядоченные очереди (по крайней мере, на стороне хоста).

person Alex    schedule 08.09.2016
comment
Спасибо, я использовал маркер в качестве первой очереди, а затем через 1 секунду после сброса + финиша, я установил его с помощью clsetusereventstatus. Он запускал все очереди ровно через 1 секунду. Я попробую то же самое с конечной точкой, чтобы получить одно событие для ожидания (используя маркер, но с событием на стороне устройства). - person huseyin tugrul buyukisik; 08.09.2016

Вы делаете это намного сложнее, чем это должно быть. В очередь записи принять событие. Используйте это как условие для вычисления в очереди вычислений и примите другое событие. Используйте это как условие чтения в очереди чтения. Нет никаких причин принудительно выполнять какую-либо другую синхронизацию. Примечание. Моя интерпретация спецификации заключается в том, что вы должны выполнить clFlush в очереди, из которой вы взяли событие, прежде чем использовать это событие в качестве условия в другой очереди.

person Dithermaster    schedule 08.09.2016
comment
Попробовал ваш способ, просто односторонняя цепочка событий на 3 очереди, драйвер не мог поместить их в перекрывающиеся позиции и в результате получить все последовательные ядра меньшего размера. Это заняло 140 мс, в то время как 1 очередь (заказ) заняла 127 мс. Когда я добавляю одно и то же событие в другую очередь (не требуется в своей собственной очереди), двустороннее событие разрешает только перекрывающиеся выполнения чтения + записи + вычисления, что приводит к 112 мс. Либо график временной шкалы CodeXL отображается неправильно, либо мой собственный таймер секундомера C# неверен, либо драйвер не способен. Он полностью завершает запись до начала вычислений. Затем вычисления заканчиваются, затем начинается чтение. Но во всех случаях события имеют задержку 1-2 мс. - person huseyin tugrul buyukisik; 11.09.2016
comment
Может быть, R7-240 не поддерживает параллелизм с 3 очередями в аппаратном обеспечении (эмулирует программное обеспечение), поскольку вычислений не существует, у него должно быть вдвое меньше времени (чтение перекрывает запись)? Может быть, только вычисление + чтение или вычисление + запись? - person huseyin tugrul buyukisik; 11.09.2016
comment
Вы были правы, не надо было усложнять. Я просто использовал 4 очереди, сделал конвейерную обработку горизонтальной, а не вертикальной, и никаких событий, она перекрывается более эффективно, показывая отсутствие пробелов между командами (также +1 очередь также добавляет немного эффективности) - person huseyin tugrul buyukisik; 14.09.2016