2012-09-14 19 views
11

Soy nuevo en C, C++ y OpenCL y estoy haciendo todo lo posible para aprenderlos en este momento. Aquí hay una función preexistente de C++ que estoy tratando de descubrir cómo portar a OpenCL usando los enlaces C o C++.¿Cómo pasar y acceder a los vectores de 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; 
    } 

}; 

En términos generales, mi pregunta es cómo pasar en todos los miembros de la clase que esta función accede a la unión y el núcleo. Entiendo cómo transmitir los valores escalares pero no estoy seguro de los valores del vector. ¿Existe alguna forma de pasar punteros a cada uno de los miembros anteriores o la memoria puede mapearlos para que la vista de OpenCL esté sincronizada con la memoria del host? Desglosado mis preguntas son las siguientes.

  1. ¿Cómo paso el miembro byc al encuadernado y al núcleo dado que estos son de tamaño variable?
  2. ¿Cómo paso el miembro d dado que es bidimensional?
  3. ¿Cómo accedo a estos miembros desde dentro del núcleo y qué tipos se declararán como argumentos en el kernel? ¿Simplemente usará la notación del índice de matriz, es decir, b [0], funciona para acceder?
  4. ¿Cómo invocaría una operación equivalente a b.size() dentro de la función kernel o no, y en su lugar pasaría el tamaño del enlace al kernel como argumento adicional? ¿Qué pasa si cambia?

Realmente agradecería la encuadernación en C o C++ y el código fuente del ejemplo del kernel en las respuestas.

Muchas gracias.

+10

'using namespace std;' - No hagas eso en un encabezado, nunca. –

+0

@EdS. ¿Por qué sería eso? – dominicbri7

+5

@ dominicbri7: Porque está contaminando el espacio de nombres global para todos los que incluyen su encabezado. Tal vez no quiero importar 'std' en mi espacio de nombres global. Tal vez hay una buena razón para eso. No fuiste y tomaste la decisión por mí. –

Respuesta

13
  1. Tiene que asignar un búfer OpenCL y copiar los datos de su CPU en él. Un búfer OpenCL tiene un tamaño fijo, por lo que tiene que volver a crearlo si el tamaño de los datos cambia o lo hace "lo suficientemente grande" y utiliza solo una subsección si se necesita menos memoria. Por ejemplo, para crear un amortiguador para b y al mismo tiempo de copiar todos sus datos en el 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 
    

    También es posible asignar directamente la memoria del host (CL_MEM_USE_HOST_PTR), pero esto impone algunas restricciones en el alineación y el acceso a la memoria del host después de crear el búfer. Básicamente, la memoria del host puede contener basura cuando no se está mapeando actualmente.

  2. Depende. ¿Los tamaños de los vectores en la segunda dimensión son consistentemente iguales? Luego, aplánelos cuando los cargue en el dispositivo OpenCL. De lo contrario, se vuelve más complicado.

  3. Declara argumentos de almacenamiento intermedio como __global punteros en su kernel. Por ejemplo, __global double *b sería apropiado para el búfer creado en 1. Simplemente puede usar la notación de matriz en el kernel para acceder a los elementos individuales en el búfer.

  4. No puede consultar el tamaño del búfer desde dentro del kernel, por lo que debe pasarlo manualmente. Esto también puede suceder implícitamente, p. si la cantidad de elementos de trabajo coincide con el tamaño de b.

Un núcleo que se puede acceder a todos los datos para el cálculo podría tener este aspecto:

__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; 
} 

Tenga en cuenta que también hay que asignar memoria para el resultado. Podría ser necesario pasar argumentos de tamaño adicional si los necesita. Llamarías al núcleo de la siguiente manera:

// 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

Muchas gracias reima. Eso ayuda mucho. Dos preguntas: (1) mis datos originales están todos en tipos de C++ como usted sabe. Pero toda la asignación de memoria en su código anterior está en cl_types. Entiendo porque. Pero, en un programa de prueba, donde paso dos vectores largos en un kernel que agrega el valor de a [0] a b [1], esto solo funciona si todos los tipos son cl_types en el programa, incluidas las declaraciones vectoriales originales que parecen extrañas los datos originales deben ser de tipo C++. ¿Que me estoy perdiendo aqui? (2) ¿Cómo uso el 'resultado' como un tipo de C++ anterior? – junkie

+2

Tienes razón, estaba un poco descuidado con los tipos allí. Mi código de ejemplo solo funcionará si 'cl_long' es del mismo tipo que' long'. Si no son lo mismo, es probable que deba realizar un paso de conversión antes de cargar los datos en el dispositivo. 'cl_long' y' cl_double' son tipos de C++ como cualquier otro, solo son typedefs. Puede usar 'result' directamente, ya que probablemente ya sea un' double'. – reima

+0

Thx. Puedo confirmar que en mi programa de prueba, el uso de punteros al vector datos no funciona. Entonces, ¿tal vez sea necesaria la conversión (crear un segundo conjunto de vectores/matrices y copiar en él?). Y establecer el resultado en una variable larga da una advertencia en VS que dice "posible pérdida de datos". cl_long en cl_platform.h se establece en 'typedef signed __int64 cl_long;' mientras que long tiene 4 bytes. Entonces, ¿no hay manera de usar todo el tiempo sin la posible pérdida de datos? – junkie

Cuestiones relacionadas