2012-09-12 17 views
6

Soy un principiante con OpenCL y tengo dificultades para entender algo. Quiero mejorar las transferencias de una imagen entre el host y el dispositivo. Hice un plan para comprenderme mejor.Transferencias superpuestas y cálculo del dispositivo en OpenCL

superior: lo que tengo ahora | Abajo: lo que quiero HtD (Host to Device) y DtH (Device to Host) son transferencias de memoria. K1 y K2 son granos.

Pensé en usar la memoria de mapeo, pero la primera transferencia (Host al dispositivo) se hace con el comando clSetKernelArg(), ¿no? ¿O tengo que cortar mi imagen de entrada en una subimagen y usar la asignación para obtener la imagen de salida?

Gracias.

Editar: imagen de entrada mem proceso K1

Más información. Imagen de salida de proceso K2 desde K1.

Por lo tanto, deseo transferir MemInput en varias piezas para K1. Y quiero leer y guardar en el host el MemOuput procesado por K2.

+2

+1 para el proceso que ha usado para hacer el diagrama :) –

Respuesta

5

Como ya habrás visto, realizas una transferencia de host a dispositivo usando clEnqueueWriteBuffer y similar.

Todos los comandos que tienen la palabra clave 'en cola' en ellos tienen una propiedad especial: Los comandos no se ejecutan directamente, pero cuando Tigger usando clFinish, clFlush, clEnqueueWaitForEvents, utilizando clEnqueueWriteBuffer en el modo de bloqueo y algunos más.

Esto significa que todas las acciones se realizan a la vez y debe sincronizarse con los objetos de evento. Como todo (puede) ocurrir a la vez, se podría hacer algo como esto (Cada punto sucede al mismo tiempo):

  1. datos transferir una
  2. Proceso de Datos Un & transferencia de datos B
  3. Proceso de Datos B & transferencia de datos C & retrive datos A '
  4. Proceso de datos C & recuperar datos B'
  5. recuperar datos C'

Recuerde: ¡Encarrar tareas sin objetos de evento puede dar como resultado la ejecución simultánea de todos los elementos puestos en cola!

Para asegurarse de que Process Data B no se produce antes de Transferir B, debe recuperar un objeto de evento de clEnqueueWriteBuffer y proporcionarlo como un objeto para esperar a f.i. clEnqueueNDRangeKernel

cl_event evt; 
clEnqueueWriteBuffer(... , bufferB , ... , ... , ... , bufferBdata , NULL , NULL , &evt); 
clEnqueueNDRangeKernel(... , kernelB , ... , ... , ... , ... , 1 , &evt, NULL); 

lugar de suministrar NULL, cada comando puede, por supuesto, esperar en ciertos objetos y generar un nuevo objeto de evento. El parámetro junto al último es una matriz, por lo que puede esperar varios eventos.


EDIT: Para resumir los comentarios a continuación Transferencia de datos - ¿Qué actos de comandos donde?

 
     CPU      GPU 
          BufA  BufB 
array[] = {...} 
clCreateBuffer() ----->[  ]    //Create (empty) Buffer in GPU memory * 
clCreateBuffer() -----> [  ][  ] //Create (empty) Buffer in GPU memory * 
clWriteBuffer() -arr-> [array] [  ] //Copy from CPU to GPU 
clCopyBuffer()   [array] -> [array] //Copy from GPU to GPU 
clReadBuffer() <-arr- [array] [array] //Copy from GPU to CPU 

* Es posible inicializar el buffer directamente por el suministro de datos utilizando el parámetro host_ptr.

+0

Ok. Pero mi problema es la transferencia de un buffer (una imagen 2D en mi caso). Un ejemplo: en mi Host, tengo un búfer que representa una matriz de 100 elementos. Pero quiero enviar 10 elementos a la vez en el dispositivo y una vez hecho esto, acepto, sincronizo para iniciar mi kernel. Quiero evitar la transferencia completa del buffer al principio (ver esquema). –

+0

Dos de los parámetros que acabo de escribir como puntos pueden recibir una matriz de tamaños, uno establece el rango, el otro establece el desplazamiento del área que desea copiar. Todavía no tengo el documento disponible, pero supongo que eso es exactamente lo que quieres. ;) – Nippey

+1

Me gusta esto: 'size_t origin [] = {0,0,0}, region [] = {2,2,1}; clEnqueueWriteImage (cola, imagen, CL_FALSE, origen, región, 0, 0, yourHostData, events_to_wait_for, new_event) 'Ahora cambie el origen y llámelo de nuevo. De esta forma puede transferir subconjuntos de su búfer. Asegúrese de que también desplace el puntero a 'yourHostData' de manera apropiada. (Nota: El origen y el desplazamiento deben ser tridimensionales. 'Region [2]' debe ser '1' para una imagen 2D – Nippey

2

Cuando crea su cola de comandos, debe habilitar la ejecución fuera de orden en sus propiedades. ver: CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, clCreateCommandQueue.

Esto le permitirá configurar sus cadenas más pequeñas de tareas y vincularlas entre sí. Todo esto se hace en el host.

anfitrión seudo código:

for i in taskChainList 
    enqueueWriteDataFromHost 
    enqueueKernel(K1) 
    enqueueKernel(K2) 
    enqueueReadFromDevice 
clfinish 

Cuando se están haciendo cola las tareas, puso el cl_event anterior en event_wait_list de cada tarea. El 'enqueueWriteDataFromHost' que he mencionado anteriormente no debería esperar a que comience otro evento.

Alternativamente,

cl_event prevWriteEvent; 
cl_event newWriteEvent; 
for i in taskChainList 
    enqueueWriteDataFromHost // pass *prevWriteEvent as the event_wait_list, and update with newWriteEvent that the enqueue function produces. Now each Write will wait on the one before it. 
    enqueueKernel(K1) 
    enqueueKernel(K2) 
    enqueueReadFromDevice //The reads shouldn't come back out of order, but they could (if the last block of processing were much faster then the 2nd-last for example) 
clfinish 
3

Muchas plataformas OpenCL no soportan fuera de orden las colas de comandos; la forma en que la mayoría de los proveedores dicen que se superponen DMA y calcular es usar colas de comandos múltiples (en orden). Puede usar eventos para asegurarse de que las dependencias se realicen en el orden correcto. NVIDIA tiene un código de ejemplo que muestra el DMA superpuesto y lo hace de esta manera (aunque no es óptimo, puede ir un poco más rápido de lo que dicen).

+0

Gracias por su respuesta. En mi caso, el cálculo del kernel OpenCL es corto. La transferencia de datos lleva mucho tiempo y es en este punto en el que quiero ganar tiempo. Lo probé en una NVIDIA GTX 260, pero esta tarjeta gráfica es incompatible con la superposición de transferencia de datos/transferencia (solo compatible con la superposición de transferencia de datos/computación). En la Guía de buenas prácticas de NVIDIA OpenCL, podemos leer: > Los dispositivos NVIDIA con capacidad de cómputo> = 2.0 poseen 2 motores de copia independientes y son capaces de copiar simultáneamente en 2 direcciones simultáneamente con el cálculo del dispositivo. Y la capacidad de cálculo GTX 260 = 1.3 ... –

2

La manera correcta (como lo hago y funciona perfectamente) es crear 2 colas de comandos, una para E/S y otra para el procesamiento. Ambos deben estar en el mismo contexto.

Puede usar eventos para controlar el horario de ambas colas, y las operaciones se ejecutarán en paralelo (si pueden). Incluso si el dispositivo no es compatible con la cola de cola, sí funciona.

Por ejemplo, puede poner en cola todas las 100 imágenes en la cola de E/S a la GPU y obtener sus eventos. Luego configure estos eventos como el desencadenante de los núcleos. Y la transferencia DtoH se desencadena por los eventos kernel. Incluso si pone en cola todos estos trabajos A LA VEZ, se procesarán en orden y con E/S paralelas.

Cuestiones relacionadas