Buenas!
Otro fin de semana que llega, cada vez hace más calor... (menos mal que ya termina el verano :-) )
Pues aquí seguimos, con la buena racha. De momento, he conseguido rehacer todo el setup de CUDA, y de momento he conseguido avanzar (no prometo un avance semanal, así que tengo alguna entrada guardada en la recámara!).
Vamos a entrar de lleno en la codificación paralela con CUDA. En concreto, empezamos con la creación de BigInteger.
¡Vamos allá!
Vamos a empezar con una pequeña introducción de conceptos y demás idiosincrácia própia de la arquitectura CUDA.
- Preámbulo: parte del código C que sirve para ajustar el contexto de CUDA. Dentro del preámbulo, realizamos las siguientes operaciones
- Crear variables para CUDA
- Copiar memoria de CPU (código C) a GPU (código CUDA)
- Llamar a la función CUDA principal
Hay que destacar la diferencia entre CPU (o código "host") y GPU (o código "device"). No es posible llamar a código "device" desde código "host", por lo que existe un tipo de función híbrido "global", que permite ejecutar código "host" y "device" de manera conjunta.
También, las funciones "device" no pueden usar funciones nativas de "host" (por ejemplo strlen), mientras que las funciones "host" no pueden usar funciones nativas de "device"; las funciones "global" pueden mezclar ambas tecnologías.
Por tanto, lo habitual es:
- Creamos variables "host" en una función puramente "host"
- Llamamos a una función "global", pasando las variables. Esta función será el preámbulo.
- La función "global" llama a las funciones "deivice"
Pensaréis que esto es todo, ¿verdad? ¡Pues no!
¿Recordáis toda la lata que os dimos con los clústeres? Pues bien, esots clústeres se definen en las llamadas a las funciones "global"; y para complicar más la cosa, una función "device" no puede llamar a una función "global" (a no ser que hagamos algunos cambios en la configuración del compilador), ya que esto es una característica avanzada llamada "palalelismo dinámico" (y que es lo que hemos diseñado).
Bien, ahora sí, estamos en disposición de ver algo de código (ya era hora)
Get Raw
0001/*
0002 * tester.c
0003 */
0004int main() {
0005 struct BigInteger* a = (struct BigInteger*)malloc(sizeof(struct BigInteger));
0006 char* s = "1234567890";
0007 int i = 0;
0008
0009 newBI_testing(a, s, 0);
0010}
Get Raw
0001/*
0002 * BigInteger.cu
0003 */
0004#include "stdio.h"
0005#include "conio.h"
0006#include "stdlib.h"
0007#include "string.h"
0008#include "BigInteger.h"
0009#include "limits.h"
0010
0011#include "cuda_runtime.h"
0012#include "device_launch_parameters.h"
0013
0014__device__ int CU_MAX_LENGTH = 4096;
0015__device__ int CU_RET = 0;
0016__device__ int BLOCK_SIZE = 256;
0017__device__ int NUM_BLOCKS = 16;
0018
0019/*
0020 * newBI_testing
0021 *
0022 * Preámbulo para pruebas
0023 */
0024void newBI_testing(void* dst, char* s, int sig) {
0025 //variables para CUDA
0026 struct BigInteger* cu_dst;
0027 char* cu_s;
0028 int* cu_sig;
0029
0030 //allocamos memoria
0031 cudaMalloc((void**)&cu_dst, sizeof(struct BigInteger));
0032 cudaMalloc((void**)&cu_s, sizeof(char) * strlen(s));
0033 cudaMalloc((void**)&cu_sig, sizeof(int));
0034
0035 //copiamos s a cu_s y sig a cu_sig
0036 cudaMemcpy(cu_s, s, sizeof(char)* strlen(s), cudaMemcpyHostToDevice);
0037 cudaMemcpy(cu_sig, &sig, sizeof(char)* strlen(s), cudaMemcpyHostToDevice);
0038
0039 //llamamos a CUDA
0040 _newBI_testing<<<1, 1>>>(cu_dst, cu_s, cu_sig);
0041
0042 //sincronizamos
0043 cudaDeviceSynchronize();
0044
0045 //copiamos de vuelta
0046 cudaMemcpy(dst, cu_dst, sizeof(struct BigInteger),
0047 cudaMemcpyDeviceToHost);
0048}
0049
0050/*
0051 * _newBI_testing
0052 *
0053 * Lanzadera para _newBI
0054 */
0055__global__ static void _newBI_testing(void* dst, char* s, int* sig) {
0056 _newBI(dst, s, sig);
0057}
0058
0059/*
0060 * Función newBI.
0061 *
0062 * Genera un nuevo dato BI a partir del string que recibe como entrada.
0063 * Se cargan en orden inverso para permitir el crecimiento de manera
0064 * sencilla.
0065 */
0066__device__ void _newBI(void* dst, char* s, int* sig){
0067 //longitud del string
0068 int i = cu_strlen(s) - 1;
0069
0070 //limpiamos el array
0071 clean<<<NUM_BLOCKS, BLOCK_SIZE>>>(dst);
0072
0073 //validamos que no sobrepase el límite establecido
0074 if (i > CU_MAX_LENGTH) {
0075 showError(1);
0076
0077 return;
0078 }
0080 //semáforo para clean.
0081 //mientras tanto, se puede ir validando la longitud (no afecta a ret)
0082 cudaDeviceSynchronize();
0083
0084 //recorremos el string y lo guardamos en integers
0085 newBI_fill<<<NUM_BLOCKS, BLOCK_SIZE >>>(dst, s, i);
0086
0087 //ajustamos la longitud
0088 ((struct BigInteger*)dst)->count = i;
0089
0090 //semáforo para newBI_fill
0091 //mientras tanto, vamos ajustando la longitud de ret
0092 cudaDeviceSynchronize();
0093
0094 //validamos signo
0095 if (*sig == -1)
0096 ((struct BigInteger*)dst)->n[((struct BigInteger*)dst)->count] *= -1;
0097}
0099/*
0100 * Función _newBI_fill.
0101 *
0102 * Rellena a.n de manera paralela a partir del string s de manera inversa
0103 * usando len como punto medio
0104 */
0105__global__ static void newBI_fill(void* va, char* s, int len) {
0106 int index = blockIdx.x * blockDim.x + threadIdx.x;
0107 int stride = blockDim.x * gridDim.x;
0108 int i = 0;
0109 char c;
0110
0111 for (i = index; i < len; i += stride) {
0112 if (i > len) {
0113 //puede que len no sea divisible por 16
0114 return;
0115 }
0116
0117 //vamos capturando los caracteres de manera inversa
0118 c = (int)(s[len - i] - 48);
0119
0120 if (c >= 0 && c <= 9)
0121 ((struct BigInteger*)va)->n[i] = c;
0122 else {
0123 showError(3);
0125 return;
0126 }
0127 }
0128}
0129
0130/*
0131 * Función clean.
0132 *
0133 * Limpia la estructura
0134 */
0135__global__ static void clean(void* va){
0136 int index = blockIdx.x * blockDim.x + threadIdx.x;
0137 int stride = blockDim.x * gridDim.x;
0138 int i = 0;
0139
0140 for (i = index; i < CU_MAX_LENGTH; i += stride)
0141 ((struct BigInteger*)va)->n[i] = 0;
0142}
0143
0144/*
0145 * Función showError.
0146 *
0147 * Muestra un error en base al índice que se le pasa
0148 */
0149__device__ static void showError(int k) {
0150 if (k == 1)
0151 printf("Error. Limite alcanzado");
0152 else if (k == 3)
0153 printf("Error. Datos erróneos en newBI");
0154 /*etc..*/
0155
0156 printf("\n");
0157
0158 //modificamos la variable de retorno para poder validarla
0159 CU_RET = k;
0160}
0161
0162/*
0163 * Función cu_strlen.
0164 *
0165 * Sinónimo de strlen C
0166 */
0167__device__ static int cu_strlen(char* s) {
0168 int ret = 0;
0169
0170 while (*s++ != '\0')
0171 ++ret;
0172
0173 return ret;
0174}
No hay comentarios:
Publicar un comentario