2017-02-07 12 views
0

Я использую opencl 1.2 C++ wrapper для моего проекта. Я хочу знать, что является правильным методом для вызова моего ядра. В моем случае у меня есть 2 устройства, и данные должны быть отправлены одновременно им.Каков правильный способ использования queue.flush() и queue.finish() после вызова ядра?

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

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

У меня есть 2 метода. Какой из них является программно правильно в моем случае:

Метод 1:

for (int i = 0; i < numberOfDevices; i++) { 
    // Enqueue the kernel. 
    kernelGA(cl::EnqueueArgs(queue[iter], 
         arguments etc...); 
    queue[i].flush(); 
} 

// Wait for the kernels to return. 
for (int i = 0; i < numberOfDevices; i++) { 
    queue[i].finish(); 
} 

Метод 2:

for (int i = 0; i < numberOfDevices; i++) { 
    // Enqueue the kernel. 
    kernelGA(cl::EnqueueArgs(queue[iter], 
        arguments etc...); 
} 

for (int i = 0; i < numberOfDevices; i++) { 
    queue[i].flush(); 
} 

// Wait for the kernels to return. 
for (int i = 0; i < numberOfDevices; i++) { 
    queue[i].finish(); 
} 

Или ни один из них правильно и есть лучший способ ждать, пока мои ядра вернутся?

+0

Способ 1 является правильным. Однако вы можете сделать это лучше. Прочтите данные из ядра в ожидании без блокировки. А потом закончите. Таким образом, оба ядра могут начать копирование данных обратно на хост, как только они закончат. – DarkZeros

ответ

1

Предполагая, что каждое устройство Вычисляет в его собственной памяти:

Я хотел бы пойти на многопоточный (для) версии петли вашего метода-1. Потому что opencl не заставляет продавцов выполнять асинхронную обработку. Например, Nvidia выполняет синхронную привязку к некоторым драйверам и аппаратным средствам, в то время как amd имеет асинхронную привязку.

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

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

Flush помогает некоторым продавцам, таким как amd, начать enqueueing Early.

Для правильного ввода и правильного вывода для всех устройств достаточно только двух команд завершения. One After Write + Compute, затем один после чтения (результаты). Таким образом, каждое устройство получает одинаковые данные о шаге и дает результаты на одном и том же шаге. Write и Compute не нуждаются в завершении между ними, если тип очереди в порядке, потому что он вычисляет один за другим. Также это не требует операций чтения, которые нужно блокировать.

Тривиальные команды отделки всегда убивают производительность.

Примечание. Я уже написал балансировщик нагрузки, используя все это, и он работает лучше. При использовании синхронизации на основе событий вместо завершения. Финиш проще, но имеет большее время синхронизации, чем одно событие.

Также одиночная очередь не всегда подталкивает gpu к ее границам. Использование по крайней мере 4 очереди на каждое устройство обеспечивает скрытие скрытия записи и вычисления в моей системе amd. Иногда даже 16 очередей помогают немного больше. Но для ситуаций с узким местом может потребоваться еще больше.

Пример:

thread1 
     Write 
     Compute 
     Synchronization with other thread 

    Thread2 
     Write 
     Compute 
     Synchronization with other thread 




    Thread 1 
     Read 
     Synchronization with other thread 

    Thread2 
     Read 
     Synchronization with other thread 

Trivial синхронизация Убивает производительность, потому что водители не знают о своем намерении, и они оставляют его, как это так, вы должны elliminate ненужных команд закончить и конвертировать блокирующие Записывает в неблокирующие те, где вы можете.

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

+0

Извините, но я обязан использовать OpenCL. –

+0

Я имел в виду opencl очереди, но в потоках вместо цикла –

+0

Или это могут быть разные процессы opencl, обменивающиеся с ramdrive, но чистые потоки быстрее. –

0

Вы должны использовать Способ 1. clFlush - единственный способ гарантировать, что команды выдаются устройству (а не только буферизуются где-то перед отправкой).