2012-09-12 12 views
6

Sono un principiante con OpenCL e ho difficoltà a capire qualcosa. Desidero migliorare i trasferimenti di un'immagine tra host e dispositivo. Ho fatto uno schema per capirmi meglio.Trasferimenti sovrapposti e calcolo del dispositivo in OpenCL

Top: quello che ho adesso | In basso: cosa voglio HtD (Host to Device) e DtH (Device to Host) sono trasferimenti di memoria. K1 e K2 sono kernel.

Ho pensato di utilizzare la memoria di mappatura, ma il primo trasferimento (da host a dispositivo) viene eseguito con il comando clSetKernelArg(), no? Oppure devo tagliare la mia immagine di input nell'immagine secondaria e utilizzare la mappatura per ottenere l'immagine di output?

Grazie.

Edit: Maggiori informazioni immagine in ingresso mem

processo di K1. Immagine di output del processo K2 da K1.

Quindi, voglio trasferire MemInput in diversi pezzi per K1. E voglio leggere e salvare sull'host il MemOuput elaborato da K2.

+2

+1 per il processo che hai utilizzato per creare lo schema :) –

risposta

5

Come forse già visto, si effettua un trasferimento da host a dispositivo utilizzando clEnqueueWriteBuffer e simili.

Tutti i comandi che hanno la parola 'accodamento' in loro hanno una proprietà speciale: I comandi non vengono eseguiti direttamente, ma quando Tigger utilizzando clFinish, clFlush, clEnqueueWaitForEvents, utilizzando clEnqueueWriteBuffer in modalità e qualche altro blocco.

Ciò significa che tutte le azioni si verificano contemporaneamente e che è necessario sincronizzarle utilizzando gli oggetti evento. Come tutto (può) accadere in una sola volta, si potrebbe fare qualcosa di simile (Ogni punto accade allo stesso tempo):

  1. trasferimento dati Un
  2. dati di processo Un & trasferimento dati B
  3. dati di processo B & trasferimento dati C & retrive dati A '
  4. dati di processo C & Recupero dati B'
  5. Recupero dati C'

Ricordare: l'accodamento di attività senza oggetti evento può comportare l'esecuzione simultanea di tutti gli elementi accodati!

Per assicurarsi che Process Data B non avvenga prima del trasferimento B, è necessario recuperare un oggetto evento da clEnqueueWriteBuffer e fornirlo come oggetto per attendere f.i. clEnqueueNDRangeKernel

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

Invece di fornire NULL, ogni comando può naturalmente attendere su alcuni oggetti e generare un nuovo oggetto evento. Il parametro next to last è un array, quindi è possibile attendere eventi per diversi eventi!


EDIT: Per riassumere i commenti qui sotto dati Trasferimento - Che comando agisce Dove?

 
     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 

* È possibile inizializzare il buffer direttamente, fornendo i dati utilizzando il parametro host_ptr.

+0

Ok. Ma il mio problema è il trasferimento di un buffer (un'immagine 2D nel mio caso). Un esempio: sul mio host, ho un buffer che rappresenta un array di 100 elementi. Ma voglio inviare 10 elementi alla volta sul dispositivo e una volta fatto, sono d'accordo, mi sincronizzo per avviare il mio kernel. Voglio evitare il trasferimento completo del buffer all'inizio (vedi schema). –

+0

Due dei parametri che ho appena scritto come punti possono ricevere una serie di dimensioni, una imposta l'intervallo, l'altra imposta l'offset dell'area che si desidera copiare. Non ho ancora il documento disponibile, ma immagino sia esattamente quello che vuoi! ;) – Nippey

+1

In questo modo: 'size_t origine [] = {0,0,0}, regione [] = {2,2,1}; clEnqueueWriteImage (coda, immagine, CL_FALSE, origine, regione, 0, 0, yourHostData, events_to_wait_for, new_event) 'Ora cambia l'origine e richiamala di nuovo. In questo modo puoi trasferire sottoinsiemi del tuo buffer. Assicurati di spostare anche il puntatore su 'yourHostData' in modo appropriato. (Nota: l'origine e l'offset devono essere tridimensionali. 'Region [2]' deve essere '1' per un'immagine 2D – Nippey

2

Quando si crea la coda comandi, è necessario abilitare l'esecuzione fuori ordine nelle proprietà. vedere: CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, clCreateCommandQueue.

In questo modo è possibile impostare le catene di attività più piccole e collegarle tra loro. Questo è tutto fatto sull'host.

ospite pseudo codice:

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

Quando si è messa in coda i compiti, mettere il cl_event precedente in event_wait_list di ogni attività. Il 'enqueueWriteDataFromHost' che ho sopra non dovrebbe aspettare l'inizio di un altro evento.

In alternativa,

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

Molte piattaforme OpenCL non supportano out-of-order code comandi; il modo in cui la maggior parte dei fornitori dichiara di eseguire DMA sovrapposti e calcola consiste nell'utilizzare più code di comando (in ordine). È possibile utilizzare gli eventi per garantire che le dipendenze vengano eseguite nell'ordine corretto. NVIDIA ha un codice di esempio che mostra DMA sovrapposti e calcola in questo modo (anche se è subottimale, può andare leggermente più veloce di quanto affermano di poterlo fare).

+0

Grazie per la risposta. Nel mio caso, il calcolo del kernel di OpenCL è breve. Il trasferimento dei dati richiede molto tempo e su questo punto voglio guadagnare tempo. L'ho provato su una NVIDIA GTX 260, ma questa scheda grafica non è compatibile con la sovrapposizione di trasferimento/trasferimento dati (compatibile solo con la sovrapposizione di trasferimento dati/elaborazione). In NVIDIA OpenCL Guida Procedure, si può leggere: > dispositivi NVIDIA con capacità di elaborazione> = 2.0 possiedono 2 motori copia indipendenti e sono in grado di copia simultanea in 2 direzioni concomitanti con computazione dispositivo. E la capacità di calcolo GTX 260 = 1,3 ... –

2

Il modo corretto (come faccio e funziona perfettamente) consiste nel creare 2 code di comandi, una per I/O e un'altra per l'elaborazione. Entrambi devono essere nello stesso contesto.

È possibile utilizzare gli eventi per controllare la pianificazione di entrambe le code e le operazioni verranno eseguite in parallelo (se possibile). Anche se il dispositivo non supporta outoforderqueue, funziona davvero.

Ad esempio, è possibile accodare tutte le 100 immagini nella coda I/O alla GPU e ottenere i relativi eventi. Quindi imposta questi eventi come trigger per i kernel. E il trasferimento DtoH viene attivato dagli eventi del kernel. Anche se si accodano tutti questi lavori una volta, verranno elaborati in ordine e con I/O parallelo.