IX. Código Bloque III: Vector de Términos Independientes

Anuncio
PROGRAMACIÓN EN EL ENTORNO CUDA EN APLICACIONES DE MECÁNICA COMPUTACIONAL
CÓDIGO DEL PROYECTO
IX.
Código Bloque III: Vector de Términos
Independientes
El objeto de esta sección es implementar un código para la última parte
del problema que corresponde a la generación del vector de elementos
independientes a partir de las condiciones de contorno definidas en la
sección de mallado y que están contenidas en los archivos neumann.dat
para las condiciones de contorno de NEUMANN y en dirichlet.dat para la
condiciones de contorno según DIRICHLET.
Inicialmente esta sección aunque constituye un cuello de botella, no es
muy dificultosa debido a:
1. Se aprovecharían trozos de código ya usados en la resolución de
los bloques anteriores.
2. No necesita mucha paralelización debido a que las condiciones
neumann y dirichlet sólo son aplicables a los elementos
frontera que son un número reducido (menos del 1% del total de
los nodos).
Sin embargo se intentó optimizar la solución mediante paralelización. En
la figura 1 se representa la estrategia usada para abordar la adaptación
de este bloque a CUDA.
GENERACIÓN DE
VECTOR DE
TÉRMINOS
INDEPENDIENTES
BLOQUE DE PARTIDA
EN CPU
LECTURA ARCHIVOS
DE CONDICIONES
DE CONTORNO
GENERACIÓN DE
VECTOR DE
TÉRMINOS
INDEPENDIENTES:
NEUMANN
LECTURA MATRIZ A
EN FORMATO MM
GENERACIÓN DE
VECTOR ES
AUXILIARES Y
VECTOR U PARA
CONDICONES DE
CÁLCULO DE B-A*U
Y GENERACION DE
B: VECTOR DE
TÉRMINOS
INDEPENDIENTES.
ADAPTACIÓN CUDA
Fig.1: Adaptación del
independientes a CUDA.
bloque
de
generación
del
vector
de
términos
157
PROGRAMACIÓN EN EL ENTORNO CUDA EN APLICACIONES DE MECÁNICA COMPUTACIONAL
CÓDIGO DEL PROYECTO
1. ALGORITMO
Este bloque pretende incorporar las condiciones NEUMANN y DIRICHLET al
problema. Parte de los archivos triangulos.dat, coordenadas.dat y en
concreto neumann.dat para NEUMANN y dirichlet.dat par DIRICHLET y genera
un vector de términos independientes.
Se calcula un vector "b" inicial con las condiciones de NEUMANN.
Posteriormente, se genera
dirichlet.dat llamado "u".
un
array
con
los
valores
ofrecidos
por
Finalmente, tomando como entrada la matriz A, calculada y generada
previamente, se calcula b-A*u y se almacena en “b” siendo este el vector
reordenado de términos independientes del sistema.
Para ello se desarrolló un código principal main. El objeto de dicho
bloque es la ejecución secuencial de los distintos pasos. El orden es el
mismo al establecido en el código matlab.
Para Cada tarea paralelizable se realiza una llamada a una función Host
que desarrolla los apartados particulares de cada sección. A partir de
estos bloques Host se realizan llamadas a los bloque kernel que serían
ejecutados en la GPU.
Se probaron, para comparar la eficiencia, kernels sencillos para rutinas
muy repetitivas como por ejemplo la inicialización de un vector. Esta
tarea permite comprobar si la inicialización de un vector asignando
valores constantes en la CPU es más o menos rápida que crear el vector
copiarlo a la GPU asignarle valores y volver a copiarlo a la CPU. Es de
relevancia recordar que existen bibliotecas que contienen codificada
esta rutina.
El programa empieza con la lectura de los archivos con datos de entradas
y almacenando esos datos en arrays apropiados. Además también se lee en
formato COO la matriz A generada en el punto anterior. Se generan
vectores auxiliares y se inicializan mediante códigos sencillos en
paralelo como medida de la eficiencia.
158
PROGRAMACIÓN EN EL ENTORNO CUDA EN APLICACIONES DE MECÁNICA COMPUTACIONAL
CÓDIGO DEL PROYECTO
i.
Para las condiciones NEUMANN:
1- Se determina el número total de elementos.
2- Se reservan los arrays apropiados. Recordemos que un lado NEUMANN
está definido por su vértice 1 su vértice 2 y el subdominio sobre el que
está definido. Resultando en un array de 3 columnas.
3- LLamada a kernel para NEUMANN. Donde el índice es el índice del lado
NEUMANN según el orden de almacenamiento en el archivo correspondiente.
Se realizan los cálculos en la GPU y se almacenan los valores en un
array inicialmente vacío.
Para seleccionar la condición apropiada según el subdominio al que se
pertenezca se usa “switch”. Se debe tener cuidado de insertar una
instrucción break detrás de cada caso en la cláusula switch.
4- Una vez devueltos los resultados en VLADO este vector es usado para
rellenar los valores del vector de términos independientes. Recordemos
que cada valor en Vlado corresponde a un lado NEUMANN definido por dos
vértices y que en B cada vértice le corresponderá un valor.
Por tanto se desdoblan los valores mediante las dos líneas de código
siguientes:
B[((int)LADO[3*i])-1]+=VLADO[i];
B[((int)LADO[3*i+1])-1]+=VLADO[i];
Al igual que en el código MATLAB de partida se actualizan los valores
acumulándolos.
ii.
Para las condiciones DIRICHLET:
Seguidamente se ejecuta
condiciones de dirichlet.
el
bloque
para
la
incorporación
de
las
Este bloque es más complicado que el anterior porque implica además del
cálculo o asignación de valores según dirichlet, la realización de la
operación b=b-Au; donde “A” es la matriz de rigidez y “u” el vector de
valores de dirichlet.
159
PROGRAMACIÓN EN EL ENTORNO CUDA EN APLICACIONES DE MECÁNICA COMPUTACIONAL
CÓDIGO DEL PROYECTO
Por tanto se divide en 3 pasos:
1- conversión de formato de A
Recordemos que la matriz A estaba almacenada en formato COO sin embargo
para usar el código de producto de matriz dispersa por vector de NVIDIA
se debe convertir la matriz a un formato más eficiente. Se eligió el
CSR.
En el código de conversión, a simple vista aparecen secciones candidatas
para ser paralelizadas por ser bucles, sin embargo el hecho de que se
acumulan
sucesivamente
arrastrando
el
dato,
implica
escrituras
concurrentes que podrían generar problemas. Esto y junto al enorme coste
que suponen las copias de memoria de CPU->GPU->CPU hizo que se
prefiriera una solución secuencial.
2- Condiciones de dirichlet: Se implementa a su vez en dos fases:
a: Generación del vector auxiliar Uaux que determina el número
total de lados dirichlet y reserva memoria para los arrays
auxiliares NODOSD y SUBDD.
En NODOSD se almacenan los vértices que forman cada lado y en
SUBDD se almacena el subdominio al que pertenece ese lado
desdoblado en vértices.
Uaux contiene en la posición i, correspondiente al nodo dirichlet
i+1, el valor del subdominio al que pertenece. En los datos de
partida se definieron 4 subdominios: 1 y 4 como lados DIRICHLET y
2 y 3 como NEUMANN.
Los vértices no dirichlet
intervienen en el proceso.
tienen
un
“0”
almacenado
y
no
Uaux actúa a modo de guía para la siguiente etapa indicando el
valor del subdominio al que pertenecen los vertices dirichlet.
b: Generación del vector “u” que almacena los valores de los nodos
dirichlet. Donde el código de esta sección es aplicable sólo a los
nodos dirichlet. Se calcula el valor correspondiente y se almacena
en el vector “u”.
3- Producto, resta y resultado final:
El producto se implementa mediante la versión CSR de SpMV de NVIDIA. Es
para aprovechar las ventajas de que A es una matriz dispersa.
Finalmente se realiza la resta que corresponde a la operación “b-Au”,
que se ha implementado usando funciones de la biblioteca CUBLAS.
160
PROGRAMACIÓN EN EL ENTORNO CUDA EN APLICACIONES DE MECÁNICA COMPUTACIONAL
CÓDIGO DEL PROYECTO
2. OBSERVACIONES
La eficiencia de este código y su rapidez no se puede explicar por la
paralelización ya que muchas tareas conllevan cálculos que afectan a
menos del 1% de los nodos del mallado (por ejemplo del orden de millares
de elementos en lugar de millones).
Una parte especialmente sensible del código ha sido la lectura de los
archivos de entrada y su conversión a formatos apropiados para la
ejecución de las operaciones necesarias, especialmente la conversión de
formato COO a CSR.
161
Descargar