2012-09-14 13 views
11

Sono nuovo di C, C++ e OpenCL e faccio del mio meglio per impararli al momento. Ecco una funzione C++ preesistente che sto cercando di capire come eseguire il porting su OpenCL usando i binding C o C++.Come passare e accedere ai vettori C++ al kernel OpenCL?

#include <vector> 

using namespace std; 

class Test { 

private: 

    double a; 
    vector<double> b; 
    vector<long> c; 
    vector<vector<double> > d; 

public: 

    double foo(long x, double y) { 
     // mathematical operations 
     // using x, y, a, b, c, d 
     // and also b.size() 
     // to calculate return value 
     return 0.0; 
    } 

}; 

In linea di massima la mia domanda è come passare in tutti i membri della classe che questa funzione accede al bind e al kernel. Capisco come passare i valori scalari ma i valori vettoriali di cui non sono sicuro. C'è forse un modo per passare i puntatori a ciascuno dei membri sopra o la memoria li mappa in modo tale che la vista di OpenCL su di essi sia sincronizzata con la memoria dell'host? Ripartiti le mie domande sono come di seguito.

  1. Come passare i membri b e c al bind e il kernel dato che questi sono di dimensioni variabili?
  2. Come passare il membro d dato che è bidimensionale?
  3. Come accedere a questi membri dal kernel e quali tipi verranno dichiarati come negli argomenti del kernel? Userà semplicemente la notazione dell'indice di array, ad esempio, b [0] per l'accesso?
  4. Come potrei invocare un'operazione equivalente a b.size() all'interno della funzione kernel o non lo farei e invece passare la dimensione dall'associazione al kernel come argomento aggiuntivo? Cosa succede se cambia?

Apprezzerei molto sia l'associazione C o C++ che il codice sorgente di esempio del codice del kernel nelle risposte.

Molte grazie.

+10

'utilizzando namespace std;' - Non farlo in un colpo di testa, mai. –

+0

@EdS. perché dovrebbe essere? – dominicbri7

+5

@ dominicbri7: perché stai inquinando lo spazio dei nomi globale per tutti coloro che includono la tua intestazione. Forse non voglio importare 'std' nel mio spazio dei nomi globale. Forse c'è una buona ragione per quello. Tu non hai fatto la scelta per me. –

risposta

13
  1. È necessario allocare un buffer OpenCL e copiare i dati della CPU in esso. Un buffer OpenCL ha una dimensione fissa, quindi è necessario ricrearlo se la dimensione dei dati cambia o la si rende "abbastanza grande" e se ne serve solo una sottosezione se è necessaria meno memoria. Ad esempio, per creare un buffer per b e contemporaneamente copiare tutti i dati al dispositivo:

    cl_mem buffer_b = clCreateBuffer(
        context, // OpenCL context 
        CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, // Only read access from kernel, 
                  // copy data from host 
        sizeof(cl_double) * b.size(), // Buffer size in bytes 
        &b[0], // Pointer to data to copy 
        &errorcode); // Return code 
    

    È anche possibile mappare direttamente memoria host (CL_MEM_USE_HOST_PTR), ma impone alcune restrizioni sulla allineamento e accesso alla memoria ospite dopo aver creato il buffer. Fondamentalmente, la memoria host può contenere dati inutili quando al momento non la si sta mappando.

  2. Dipende. Le dimensioni dei vettori nella seconda dimensione sono costantemente uguali? Quindi appiattiscili quando li carichi sul dispositivo OpenCL. Altrimenti diventa più complicato.

  3. Si dichiarano gli argomenti del buffer come puntatori __global nel kernel. Ad esempio, __global double *b sarebbe appropriato per il buffer creato in 1. È possibile utilizzare semplicemente la notazione array nel kernel per accedere ai singoli elementi nel buffer.

  4. Non è possibile interrogare la dimensione del buffer all'interno del kernel, quindi è necessario passarlo manualmente. Ciò può anche accadere implicitamente, ad es. se il numero di elementi di lavoro corrisponde alla dimensione di b.

un kernel che può accedere a tutti i dati per il calcolo potrebbe assomigliare a questo:

__kernel void foo(long x, double y, double a, __global double* b, int b_size, 
        __global long* c, __global double* d, 
        __global double* result) { 
    // Here be dragons 
    *result = 0.0; 
} 

noti che si hanno anche per allocare memoria per il risultato. Potrebbe essere necessario passare argomenti di dimensioni aggiuntive nel caso in cui ne avessi bisogno. Si potrebbe chiamare il kernel come segue:

// Create/fill buffers 
// ... 

// Set arguments 
clSetKernelArg(kernel, 0, sizeof(cl_long), &x); 
clSetKernelArg(kernel, 1, sizeof(cl_double), &y); 
clSetKernelArg(kernel, 2, sizeof(cl_double), &a); 
clSetKernelArg(kernel, 3, sizeof(cl_mem), &b_buffer); 
cl_int b_size = b.size(); 
clSetKernelArg(kernel, 4, sizeof(cl_int), &b_size); 
clSetKernelArg(kernel, 5, sizeof(cl_mem), &c_buffer); 
clSetKernelArg(kernel, 6, sizeof(cl_mem), &d_buffer); 
clSetKernelArg(kernel, 7, sizeof(cl_mem), &result_buffer); 
// Enqueue kernel 
clEnqueueNDRangeKernel(queue, kernel, /* ... depends on your domain */); 

// Read back result 
cl_double result; 
clEnqueueReadBuffer(queue, result_buffer, CL_TRUE, 0, sizeof(cl_double), &result, 
        0, NULL, NULL); 
+0

Grazie mille reima. Questo aiuta molto. Due domande: (1) i miei dati originali sono tutti in tipi C++ come sai. Ma tutta l'allocazione di memoria nel tuo codice sopra è in cl_types. Capisco perché. Ma, in un programma di test, dove passo due lunghi vettori in un kernel che aggiunge il valore da [0] a b [1], funziona solo se tutti i tipi sono cl_types nel programma incluse le dichiarazioni vettoriali originali che sembrano strane come i dati originali devono essere di tipo C++. Cosa mi manca qui? (2) Come si usa il 'risultato' come un tipo C++ sopra? – junkie

+2

Hai ragione, ero un po 'sciatto con i tipi lì. Il mio codice di esempio funzionerà solo se 'cl_long' è dello stesso tipo di' long'. Se non sono uguali, probabilmente dovrai eseguire un passaggio di conversione prima di caricare i dati sul dispositivo. 'cl_long' e' cl_double' sono tipi C++ come qualsiasi altro, sono solo typedef. Puoi usare 'result' direttamente, poiché probabilmente è già un' double'. – reima

+0

Thx. Posso confermare che nel mio programma di test, l'utilizzo di puntatori per il vettore dei dati non funziona. Quindi forse è necessaria la conversione (creare un secondo set di vettori/array e copiarli?). E impostare il risultato in una variabile lunga dà un avvertimento in VS dicendo "possibile perdita di dati". cl_long in cl_platform.h è impostato a 'typedef signed __int64 cl_long;' mentre long è 4 byte. Quindi forse non c'è modo di usare il tempo senza una possibile perdita di dati? – junkie

Problemi correlati