2016-10-14 3 views
0

У меня есть два графических процессора, одно ядро, один контекст и две очереди команд (по 1 на каждый графический процессор). Я попытался запустить их в цикле, где запущена каждая очередь команд, а затем я попробовал как queue.finish(), так и queue.flush() в надежде запустить работу на графических процессорах одновременно.Как запустить OpenCL на нескольких графических процессорах (2) одновременно?

Но на самом деле происходит то, что данные сначала отправляются на одно устройство, а GPU выполняет свою работу, а затем другой графический процессор начинает работать. Это занимает в два раза больше времени, чем на одном графическом процессоре. Это не то, что я намереваюсь достичь!

Хотя я также читаю буферы обратно в код хоста, и можно подумать, что это может быть проблемой для второго GPU, ожидающего результата 1го. Но я также прокомментировал результаты чтения без ведома. Его все равно то же самое.

for (unsigned int iter = 0; iter < numberOfDevices; iter++) { 
    // Load in kernel source, creating a program object for the context 
    cl::Program programGA(context, stringifiedSourceCL, true); 

    // Create the kernel functor 
    auto kernelGA = cl::make_kernel<cl::Buffer, 
            cl::Buffer, 
            cl::Buffer> 
            (programGA, "kernelGA"); 

    // CREATE THE BUFFERS. 

    d_pop = cl::Buffer(context, CL_MEM_READ_WRITE | CL_MEM_USE_HOST_PTR, 
         (Length * POP_SIZE * sizeof(double)), 
         pop); 
    // And other buffers... 

    // Enqueue the kernel. 
    kernelGA(cl::EnqueueArgs(queue[iter], 
          cl::NDRange(POP_SIZE/numberOfDevices)), 
          d_integerParameters, 
          d_doubleParameters, ... and so on...); 

    // Enqueue in the corresponding device. 
    queue[iter].flush(); 

    // Get results from the queue. 
    queue[iter].enqueueReadBuffer(buf_half_population, 
             true, 
             0, 
             popSizeMD * sizeof(double), 
             popMD[iter]); 

    // Add up the results after every iteration. 
    for (int in_iter = 0; in_iter < populationSizeMD; in_iter++, it_j++) { 
     population[it_j] = populationMD[iter][in_iter]; 
    } 
} 

Мой вопрос: Что я должен сделать для того чтобы достигнуть истинного Параллелизм и делают графические процессоры работают одновременно, не дожидаясь результата другого? Должен ли я создать два контекста? Должен ли я делать что-то еще?

Имея в виду, что существует одно ядро ​​

ответ

0

ClFinish является блокирование команды.

Для всех очередей после очередей всех команд для всех очередей требуется параллельный параллельный прием + несколько контекстов (по 1 для каждого устройства) или задержка флеша/завершения для всех очередей.

Для принимающей стороне параллелизм,

Преобразовать

for (unsigned int iter = 0; iter < numberOfDevices; iter++) {...} 

в

Concurrent.for(){} // if there is any for the language you working on 

Parallel.For(0,n,i=>{...}); // C# 

версий поэтому каждая итерация одновременно. Например, Parallel.For работает в C#. Тогда будьте уверены, что работаете с разными диапазонами массивов, поэтому действия копирования буфера не совпадают. Если есть головоломка с пропускной способностью pci-e, вы можете скопировать на gpu-1 на первой итерации, вычислить на gpu-1 + copy на gpu-2 на второй итерации, получить результаты от gpu-1 и вычислить на gpu-2 на третьем итерации, получить результаты от gpu-2 при последней итерации. Если нет голодания, вы можете делать все копии + все Вычисляет + все результаты в разных петлях, как:

Parallel.For(... copy to gpus) 
sync_point() ---> because other gpus result can change some input arrays, 
      need to be sure all gpus have their own copies/buffers updated 
      but not needed if it is an embarrassingly parallel workload 
Parallel.For(... compute on gpus + get results) 

Для отсроченного финишного/флеша:

for(){...} // divide work into 4-8 parts per gpu, 
       so all gpu can have its turn without waiting much 
       computing concurrently between mgpus 
flush1       
flush2 
finish1 
finish2 

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

Первый тип для меня легче, потому что я могу получить лучшие временные данные для каждого устройства, чтобы выполнить балансировку работы по всему gpus (а не просто разделить ее наполовину, изменяя соответственно время, затрачиваемое на каждый gpu, буферные копии и диапазон работ) , Но второй тип должен быть быстрее, если драйверы лучше управляют копиями. Особенно, если вы делаете map/unmap вместо write/read, потому что map/map использует dma-движки вместо процессора при получении результатов или копировании в gpu.

Смежные вопросы