Intro
Contents
gpu_buffer è, come evidenziato nel nome, il gestore di un buffer di memoria per i/o su una GPU; appartiene al gruppo delle classi derivate da gpu_mem_obj, e gestisce un blocco di memoria assegnata ad un context OpenCL. Come tutte le altre classi del namespace OPCL è stata progettata per semplificare l’uso delle API OpenCL. In sintesi, gpu_buffer è la classe che gestisce la corrispondenza fra un blocco di memoria su CPU ed il suo i/o sulle GPU gestite dalle API OpenCL. nascondendo al programmatore tutte le complessità legate a queste operazioni.
class AFX_EXT_CLASS gpu_buffer : public gpu_mem_object { public: gpu_buffer ( ); ~gpu_buffer ( ); bool Init ( class gpu_kernel *kernel, cl_uint memsize, cl_mem_flags mode = CL_MEM_READ_ONLY, void *host_buf=0 ); void *Ptr ( ); size_t Bytes ( ); bool Push_kernel(gpu_kernel *kernel = 0); __use_private_struct };
void *Ptr() e size_t Bytes()
le due funzioni Ptr() e Bytes() rimandano il puntatore della memoria host e le dimensioni della memoria allocata. La memoria host è allocata sulla CPU e può essere definita esternamente alla classe GPU, assegnando il puntatore attraverso la funzione gpu_buffer::Init, o allocata automaticamente nella gpu_buffer, ed accedendovi attraverso la funzione Ptr(). di seguito, in questo articolo, vedremo come ( e perchè) utilizzare questi due diversi metodi di allocazione.
La classe contiene un puntatore alla memoria allocata su CPU, e nasconde un corrispondente handle OpenCL cl_mem definito attraverso la funzione OpenCL clCreateBuffer: l’handle cl_mem è accessibile mediante la funzione Get_id () ereditata da gpu_mem_object, ma non sarà mai necessario utilizzare informazioni e funzioni OpenCL direttamente, a meno che non si debba costruire nuove classe del namespace OPCL,
bool Init()
La prima cosa da notare è il primo parametro della funzione , gpu_kernel *kernel; ciò può apparire strano a chi ha dimestichezza con le API OpenCL, poiché la memoria è sempre gestita su un context, ed in seguito eventualmente passata ad un kernel eseguito su GPU: poichè un kernel è sempre associato ad un programma , a sua volta associato ad un context, è evidente che l’associazione fra gpu_buffer e context è eseguito nella Init in modo completamente nascosto. Vedremo che questa scelta non comporta alcuna limitazione alla programmazione, anzi semplifica l’interfaccia di programmazione.
Il secondo parametri della funzione è memsize, cioè la dimensione del buffer host da gestire; in un prossimo articolo sulla creazione di nuove classi derivate da gpu_buffer vedremo che questo parametro, per un uso particolare è passato a o, e ne analizzeremo il significato; per l’uso standard invece memsize deve essere >0;
il terzo parametro cl_mem_flags mode è uno dei tipi numerali di OpenCL, e definisce il tipo di trattamento che del buffer dovrà fare l’API OpenCL; i valori possibili sono
CL_MEM_READ_WRITE CL_MEM_WRITE_ONLY CL_MEM_READ_ONLY CL_MEM_USE_HOST_PTR CL_MEM_ALLOC_HOST_PTR CL_MEM_COPY_HOST_PTR
Per un’approfondimento del significato di questi valori leggere qui.
Il quarto ed ultimo parametro è host_buf, un puntatore alla memoria che definisce la memoria allocata sulla CPU da associare alla host_buf lato GPU;
bool Push_kernel()
La funzione è virtuale ed in genere ridefinita per le classi derivate da gpu_buffer; attraverso questa funzione viene trasferita l’informazione organizzata nella gpu_buffer (o classi derivate) nella queue dei parametri del kernel. In genere non è necessario ridefinire il kernel, in quanto già assegnato nella funzione Init(); tuttavia è possibile passare gli stessi dati ad un kernel aggiuntivo da eseguire in parallelo o in sequenza con quello di default;
Compiti svolti da Push_kernel
Per capire come la funzione trasferisca le informazioni nella coda dei parametri del kernel, viene di seguito riportato il codice della funzione di gpu_buffer;
bool gpu_buffer::Push_kernel(gpu_kernel *kernel) { __getstr _test_alt_kernel return ( kernel->PushArg(this) && kernel->PushArg(_$str->bytes) // numero di byte inseriti ); }
la macro _test_alt_str permette di assegnare un kernel alternativo, eventualmente passato alla funzione, e la sua discussione non serve a questa esposizione; la riga kernel->Push(arg) trasferisce il buffer allocato in gpu_buffer (quello ottenibile dalla funzione gpu_buffer::Ptr() sulla coda dei parametri del kernel; con metodo simile le dimensioni del buffer sono aggiunte alla coda come secondo parametro;