Back to Convolution Clase 16, 1/6/2014 http://fisica.cab.cnea.gov.ar/gpgpu/index.php/en/icnpg/clases Correlación (o convolución) 1D [g1ej5] Señal Filtro Señal filtrada en n = Area pintada Correlación (o convolución) 1D [g1ej5] Para pensar … ¿ Que significa aplicar este filtro particular ? Stencils Correlación (o convolución) 1D [g1ej5] Simple versión serial... /* convolucion en la cpu: requiere dos loops … */ void conv_cpu(const FLOAT* input, FLOAT* output, const FLOAT* filter) { FLOAT temp; for(int j=0;j<N;j++){ temp=0.0; for(int i=0;i<Nh;i++){ temp += filter[i]*input[i+j]; } output[j] = temp; } } Ojo... void conv_gpu(const FLOAT* d_input, FLOAT* d_output, const FLOAT* d_filter) { ¿ Simple versión paralela ? ¿....? } Correlación (o convolución) 1D [g1ej5] Simple versión serial... /* convolucion en la cpu: requiere dos loops … */ void conv_cpu(const FLOAT* input, FLOAT* output, const FLOAT* filter) { FLOAT temp; for(int j=0;j<N;j++){ temp=0.0; for(int i=0;i<Nh;i++){ temp += filter[i]*input[i+j]; } output[j] = temp; } } void conv_gpu(const FLOAT* d_input, FLOAT* d_output, const FLOAT* d_filter) { ¿ Simple versión paralela ? ¿como reparto los datos entre los threads? ¿como reparto la lectura y la escritura? ¿ que loop paralelizo ?... ¿ ambos ? } Convolución // convolucion usando indexado unidimensional de threads/blocks // un thread por cada elemento del output (la mas simple!) // todo en memoria global // lanzamiento: la grilla se puede elegir independiente de N __global__ void conv_one_thread_per_output_element_all_global (const FLOAT* input, FLOAT* output, const FLOAT* filter) { int j = blockIdx.x * blockDim.x + threadIdx.x; FLOAT temp; while(j<N) { temp=0.0; for(int i=0;i<Nh;i++){ temp += filter[i]*input[i+j]; } output[j]=temp; j+=gridDim.x*blockDim.x; } } Como lo haria usando thrust::transform?. Convolución // convolucion usando indexado unidimensional de threads/blocks // un thread por cada elemento del output // filtro en memoria constante, el resto en global // lanzamiento: la grilla se puede elegir independiente de N __constant__ FLOAT d_filtro_constant[Nh]; __global__ void conv_one_thread_per_output_element_filter_in_constant (const FLOAT* input, FLOAT* output) { int j = blockIdx.x * blockDim.x + threadIdx.x; FLOAT temp; while(j<N) { temp=0.0; for(int i=0;i<Nh;i++){ temp += d_filtro_constant[i]*input[i+j]; } output[j]=temp; j+=gridDim.x*blockDim.x; } } Como lo haria usando thrust::transform?. Convolución __constant__ FLOAT d_filtro_constant[Nh]; struct conv_one_thread_per_output_element_filter_in_constant_functor { FLOAT* senial; conv_one_thread_per_output_element_filter_in_constant_functor (FLOAT* input) { senial=input; }; __device__ FLOAT operator()(const int j) { FLOAT temp; temp=0.0; for(int i=0;i<Nh;i++){ temp += d_filtro_constant[i]*senial[i+j]; } return temp; } }; MAIN // hacerlo con un transform de thrust... thrust::transform( thrust::cuda::par, thrust::make_counting_iterator(0),thrust::make_counting_iterator(N), d_output, conv_one_thread_per_output_element_filter_in_constant_functor(d_input) ); Convolucion 1d Códigos: g1ej5_sol.cu ● ● ● cp -r /share/apps/codigos/alumnos_icnpg2015/convolucion_experimental . cd convolucion_experimental Make; qsub submit.sh #caso Casos para analizar (cual gana?) 1) Filtro y señal en memoria global 2) Filtro en memoria constante, señal en global 3) Filtro en shared, señal en global 4) Filtro y señal en shared 5) Filtro en constante, señal en shared 6) Filtro en registro y señal en global 7) Usando cufft y teorema de la convolucion 8) Filtro en constante y señal en global, usando thrust 9) TODO: hacerlo con CUSP. 10) Filtro y señal en global, 1 thread por * usando atomics 11) Filtro en constante y señal en global, 1 thread por * usando atomics Convolucion 1d en CUSP filtro filtro filtro …... input …... = output …... filtro El operador es ralo para filtros chicos, pero su matriz es muy redundante! Operadores lineales en CUSP (matrix free!) int main(void) { // number of grid points in each dimension const int N = 10; // create a matrix-free linear operator stencil A(N); // allocate storage for solution (x) and right hand side (b) cusp::array1d<float, cusp::device_memory> x(A.num_rows, 0); cusp::array1d<float, cusp::device_memory> b(A.num_rows, 1); // set stopping criteria: // iteration_limit = 100 // relative_tolerance = 1e-6 cusp::monitor<float> monitor(b, 100, 1e-5, 0, false); // solve the linear system A * x = b with the Conjugate Gradient method cusp::krylov::cg(A, x, b, monitor); return 0; } Operadores lineales en CUSP (matrix free) class stencil : public cusp::linear_operator<float,cusp::device_memory> { public: typedef cusp::linear_operator<float,cusp::device_memory> super; int N; // constructor stencil(int N) : super(N*N,N*N), N(N) {} // linear operator y = A*x template <typename VectorType1,typename VectorType2> void operator()(const VectorType1& x, VectorType2& y) const { // obtain a raw pointer to device memory const float * x_ptr = thrust::raw_pointer_cast(&x[0]); float * y_ptr = thrust::raw_pointer_cast(&y[0]); dim3 dimBlock(16,16); dim3 dimGrid((N + 15) / 16, (N + 15) / 16); stencil_kernel<<<dimGrid,dimBlock>>>(N, x_ptr, y_ptr); } }; Operadores lineales en CUSP (matrix free!) ● ● ● ● Códigos: stencil.cu cp -r /share/apps/codigos/cusplibrary-master/examples/LinearOperator . cd LinearOperator nvcc stencil.cu -I /share/apps/codigos/cusplibrary-master/ qsub submit.sh // // // // // // // // // // // // // This example shows how to use cusp::linear_operator to solve a linear system with a user-defined linear operator A. The linear_operator is a way to interface custom sparse matrix formats or so-called "matrix-free" methods with the iterative solvers in Cusp. In this example, we illustrate a matrix-free implementation of a simple 5-point finite-difference stencil, [ 0 -1 0 ] [ -1 4 -1 ] [ 0 -1 0 ] using a CUDA kernel. We combine the linear_operator with the Conjugate Gradient method to solve a 2D Poisson problem.