Está en la página 1de 14

Calculo de pi a traves de distintas

herramientas de computacion
paralela

Daniel Felipe Bueno Calderon

16 de marzo de 2017

0.1. Introducci
on
Como herramienta para la computacion paralela es-
ta guia pretende mostrar fundamentos basico de distintos
mecanismos en los que se desarrolla este paradigma, para
ello partimos que entre todas estas herramientas desarro-
llen la misma funcion, que en este caso sera el calculo del
numero pi, para que de este modo se pueda explicar cada
una de ellas por separado y futuramente realizarse una
comparacion con respecto a su proceso y el numero de
hilos que cada uno de estos lenguajes maneje.

1
2

0.2. Calculo de pi
En matematicas, la formula de Leibniz para el calculo
de , nombrada as en honor a Gottfried Leibniz, dice
que:

4
= 1 31 + 15 17 + 91 ...

La expresion anterior es una serie infinita denominada


serie de Leibniz, que converge a 4 . Usando una sumatoria
, la serie se puede expresar como


X (x)n
=
4 n=0 2n + 1

La simplicidad de la ecuacion anterior permite una faci-


lidad al representar en algun lenguaje de alto nivel.

Calculo de pi de manera secuencial


Gracias a la formula d Leibniz, se va a desarrollar el
calculo de pi a traves de lenguaje C.
#include<s t d i o . h>
#include<math . h>
int n =10000000;
int main ( )
{
int i ;
3

long double s =0;


s c a n f ( %d ,&n ) ;
f o r ( i =0; i <n ; i ++){
s+=(long double ) pow( 1 , i ) / ( 2 i +1) ;
}
p r i n t f ( %Lf \n , s ) ;
return 0 ;
}

En general, el codigo anterior toma la formula de


Leibniz, y la representa como un ciclo for, que dentro
del bucle, va asignadole los valores a la variable s, donde
en cada iteracion realiza casting a long double, para que
al final se le suma a la variable s.

0.3. Posix
Posix es una herramienta que permite realizar proce-
sos en paralelo, a traves de su menejo de hilos, imple-
mentando la libreria <pthread.h> a traves del lenguaje
C/C++. En el calculo de pi se implementa en posix de
la siguiente manera:

#define NUM 2
double p i [NUM] ;
struct g {
int pos , i n i , f i n ;
};
4

En el codigo anterior se ve la definicion de NUM, una


variable que va manejar la cantidad de hilos, la estructura
g que contiene las variables que iran como parametro
en la funcion a paralelizar y la variable global pi que
contendra el valor resultado

void c a l c u l a r P i ( void a ) {
int i ;
struct g l = ( struct g ) a ;
i = l >i n i ;
f l o a t s i g n o = 1;
i f ( i % 2 == 0 ) {
signo = 1;
}
do {
p i [ l >pos ] = p i [ l >pos ] + ( s i g n o / ( ( 2
i ) + 1) ) ;
i ++;
s i g n o = 1;
} while ( i < l >f i n ) ;
}

Definiendo la funcion calcularPi, siendo el metodo que


implementara cada uno de los hilos, esta funcion recibe
como parametro de entrada el apuntador a de tipo void,
realizandole un casting convirtiendo la variable al tipo
de la estructura g previamente definida. Le asignamos a
i el valor ini, segmentado as el hilo a los parametros ini
y fin de la estructura g. Se determina el valor de signo
en un condicional y posteriormente se calcula pi en las
5

posicion pos, que es el hilo que estamos modificando en


estos momentos.
Dentro de la funcion main(), se crea la paralelizacion
deseada de la siguiendo estos pasos:

1. Creacion del arreglo de tipo pthread t hilo de


tamano NUM, que contendra al numero de hilos
que el programa implementara.Ademas de creacion
de variables necesarias para su ejecucion
int i , l ;
p t h r e a d t h i l o [NUM] ;
int a , x ;
int aux = 1 e09 / NUM;

2. Declaracion de la estructura g, asignando sus pa-


rametros, y creando un espacio de memoria con el
comando malloc()
struct g k = m a l l o c ( s i z e o f ( struct g ) ) ;
k>pos = i;
k>i n i = aux i ;
k>f i n = aux ( i + 1 ) ;

3. Implentacion del proceso de los hilos con el coman-


do pthread create() que recibe como parametros
de entrada el hilo que maneja la funcion, un valor
NULL, el metodo a paralelizar, y los parametros
necesarios para ese metodo, que en este caso sera a
estructura previamente declarada
6

l = p t h r e a d c r e a t e (& h i l o [ i ] , NULL, ( void )


c a l c u l a r P i , ( void ) k ) ;

4. Con la funcion pthread join () suspende la ejecu-


cion del subproceso de llamada hasta que finaliza
el subproceso de destino, a menos que el subproceso
de destino ya haya finalizado.Seguidamente realiza-
mos la suma de los valores que tenian en cada uno
de los hilos de ejecucion en la variable result, que
muestra el resultado deseado.
f o r ( i = 0 ; i < NUM; i ++) {
l = p t h r e a d j o i n ( h i l o [ i ] , ( void )
&x ) ;
i f ( l != 0 ) {
perror ( error ) ;
}
}
double r e s u l t = 0 ;
f o r ( i = 0 ; i < NUM; i ++) {
r e s u l t += p i [ i ] ;
}
p r i n t f ( PI= %.24 f \n , 4 r e s u l t ) ;
7

0.4. CUDA
CUDA es una arquitectura de calculo paralelo de NVI-
DIA que aprovecha la gran potencia de la GPU (unidad
de procesamiento grafico) para proporcionar un incre-
mento extraordinario del rendimiento del sistema. CUDA
proporciona unas cuantas extensiones de C y C++ que
permiten implementar el paralelismo en el procesamiento
de tareas y datos con diferentes niveles de granularidad.
El programador puede expresar ese paralelismo mediante
diferentes lenguajes de alto nivel como C, C++.
CUDA maneja el concepto de bloque, sientdo este una
agrupacion de hilos para asi compratir informacion entre
ellos de manera facil y dinamica.
En el calculo de pi el codigo contara con la funcion
global calculatePi que es la funcion a paralelizar, en
ella determina el hilo que se esta ejecutando, por medio
del id del bloque que lo maneja y la dimension de esta
a traves de(blockDim.x * blockIdx.x) + threadIdx.x. Se-
Determina el indice delimitando el funcinamiento del por
medio de las variables initIterator y endIterator. Den-
tro del ciclo do()while se realizan dos sumas una positi-
va y otra negativa imitando la formula Leibiniz, gracias
a que dentro del bucle las variable i se incrementa dos
veces, obteniendo el valor definitvo se asigna al arreglo
piTotal en la posicion index, para concluir con la sin-
cronizacion de hilos syncthreads(); que consiste en
convertir la posicion 0 de piTotal como el acumulador
8

de todos los valores del arreglo.


global void c a l c u l a t e P i ( double p i T o t a l ,
long int i t e r a t i o n s , int t o t a l T h r e a d s )
{ long int i n i t I t e r a t i o n , e n d I t e r a t i o n ;
long int i = 0 ;
double p i P a r t i a l ;
int i n d e x = ( blockDim . x b l o c k I d x . x ) +
threadIdx . x ;
initIteration = (
i t e r a t i o n s / totalThreads ) index ;
endIteration = initIteration + ( iterations /
totalThreads ) 1;
i = initIteration ;
piPartial = 0;
do{
p i P a r t i a l = p i P a r t i a l + ( double ) ( 4 . 0 /
( ( i 2 ) +1) ) ;
i ++;
p i P a r t i a l = p i P a r t i a l ( double ) ( 4 . 0 /
( ( i 2 ) +1) ) ;
i ++;
} while ( i < e n d I t e r a t i o n ) ;
piTotal [ index ] = p i P a r t i a l ;

syncthreads () ;
i f ( i n d e x == 0 ) {
f o r ( i = 1 ; i < t o t a l T h r e a d s ; i ++)
piTotal [ 0 ] = piTotal [ 0 ] + piTotal [ i
];
}
}

Dentro de la funcion main() se inicializan variables


9

importantes como el numero de bloques blocksPerGrid,


el numero de hilos por bloque blocksPerGrid, se crea
pi total teniendo una varibale en el dispositivo(device), la
otra como alfitriona(host) siendo *d pitotal y *h pitotal
respectivamente, para procede a generar un espacio de
memoria con malloc dandole el valor de 0 a cada posi-
cion.
int b l o c k s P e r G r i d , t h r e a d s P e r B l o c k , i , s i z e ;
long int i t e r a t i o n s ;
int t o t a l T h r e a d s ;
double h p i t o t a l , d p i t o t a l ;

s s c a n f ( argv [ 1 ] , %i , &b l o c k s P e r G r i d ) ;
cudaError t e r r = cudaSuccess ;

s i z e = s i z e o f ( double ) NUMTHREADS;
h p i t o t a l = ( double ) m a l l o c ( s i z e ) ;

Se crea el kernel una funcion la cual al ejecutarse lo hara


en N distintos hilos en lugar de en secuencial. se llamala
funcion a paralelizar y se le asigna el n
umero de bloquue,
los hilos por bloque y los respectivos parametros que re-
cibe el metodo para su proceso.
// Lanzar KERNEL
t h r e a d s P e r B l o c k = NUMTHREADS/ b l o c k s P e r G r i d ;
totalThreads = blocksPerGrid
threadsPerBlock ;
i t e r a t i o n s = ITERATIONS ;
p r i n t f ( CUDA k e r n e l l a u n c h with %d b l o c k s o f
%d t h r e a d s T o t a l : %i \n , b l o c k s P e r G r i d ,
10

threadsPerBlock , totalThreads ) ;
c a l c u l a t e P i <<<b l o c k s P e r G r i d , t h r e a d s P e r B l o c k
>>>(d p i t o t a l , i t e r a t i o n s , t o t a l T h r e a d s )
;
e r r = cudaGetLastError ( ) ;
i f ( e r r != c u d a S u c c e s s ) {
f p r i n t f ( stderr , Failed to launch
vectorAdd k e r n e l ( e r r o r code %s ) ! \ n
, cudaGetErrorString ( err ) ) ;
e x i t (EXIT FAILURE) ;
}

e r r = cudaMemcpy ( h p i t o t a l , d p i t o t a l , s i z e ,
cudaMemcpyDeviceToHost ) ;
i f ( e r r != c u d a S u c c e s s ) {
f p r i n t f ( s t d e r r , F a i l e d t o copy v e c t o r C
from d e v i c e t o h o s t ( e r r o r code %s )
! \ n , c u d a G e t E r r o r S t r i n g ( e r r ) ) ;
e x i t (EXIT FAILURE) ;
}

e r r = cudaFree ( d p i t o t a l ) ;
i f ( e r r != c u d a S u c c e s s ) {
f p r i n t f ( stderr , Failed to f r e e device
v e c t o r C ( e r r o r code %s ) ! \ n ,
cudaGetErrorString ( err ) ) ;
e x i t (EXIT FAILURE) ;
}

p r i n t f ( \n %.12 f , h p i t o t a l ) ;
// Free h o s t memory

free ( h pitotal ) ;
11

e r r = cudaDeviceReset ( ) ;
i f ( e r r != c u d a S u c c e s s ) {
f p r i n t f ( stderr , Failed to d e i n i t i a l i z e
t h e d e v i c e ! e r r o r= %s \n ,
cudaGetErrorString ( err ) ) ;
e x i t (EXIT FAILURE) ;
}

0.5. OpenMP
OpenMP es una interfaz de programacion de apli-
caciones (API) para la programacion multiproceso de
memoria compartida en m ultiples plataformas. Permite
a
nadir concurrencia a los programas escritos en C y C++
y Fortran sobre la base del modelo de ejecucion fork-join.
Se compone de un conjunto de directivas de compilador,
rutinas de biblioteca, y variables de entorno que influyen
el comportamiento en tiempo de ejecucion.
Para el desarrollo de pi con OpenMP se procede de
la sigiuiente manera:
#include <omp . h>
s t a t i c long num steps = 1 0 0 0 0 0 ;
double s t e p ;
#define NUM THREADS 2
void main ( ) {
int i , n t h r e a d s ; double pi , sum [NUM
THREADS ] ;
s t e p = 1 . 0 / ( double ) num s t e p s ;
o m p s e t n u m t h r e a d s (NUM THREADS) ;
12

#p r a g m a o m p p a r a l l e l {
int i , id , n t h r d s ;
double x ;
i d = omp get thread num ( ) ;
n t h r d s = omp get num threads ( ) ;
i f ( i d == 0 ) n t h r e a d s = n t h r d s ;
f o r ( i=id , sum [ i d ] = 0 . 0 ; i <
num steps ; i=i+n t h r d s ) {
x=( i +0.5) s t e p ;
sum [ i d ] += 4 . 0 / ( 1 . 0 + xx )
;
}
}
f o r ( i =0, p i = 0 . 0 ; i <n t h r e a d s ; i ++)p i += sum
[ i ] step ;
}

El codigo mostrado anteriormente muestra e dessarrollo


de pi, para entenderlo partiremos por definicir funciones
Especiales de OpenMp seguido del comportamiento del
algoritmo.

la libreria <omp.h> permite cualquier programa


hecho en C/C++ realizar procesamiento en para-
lelo.
omp set num threads(NUM THREADS), asigna el
n
umero de hilos de ejecucion que utiliara el progra-
ma
#pragma omp parallel{ ... } Todo lo que este den-
tro del los corchetes sera el fragmento de codigo a
13

paralelizar
omp get thread num() Obtiene la cantidad de hilos
en el programa para asignarlos a una variable.

El codigo funciona definiendo variables que determi-


nan el espacio donde se hara la formula de Leibniz, el
numero de hilos, la cantidad de iteraciones o pasos que
va a realizar. ASigna el n
umero de hilos determinando el
valor de cada paso dividendo 1 en el total de los pasos
1
(step = steps ) y asigna la cantidad hilos que el sistema
utilizara.
Cada hilo define la variable x que toma el valor de
paso y lo multiplica por el indice de la iteracion, seguida-
mente toma el valor x y lo suma al arreglo que contiene
los resultado, para al final sumarlos y obternet el valor
de pi.

0.6. Conclusiones
Gracias a las distintos a las distintas herramientas uti-
liadas en computacion paralela, el calculo de pi se puede
realizar en multiples plataformas, no cambiando su com-
portamiento matematico de ser una serie geometrica.
Muchas herramientas de la computacion paralela estan
implementadas en C/C++ gracias al manero de apunta-
dores y manejo de memoria esencial para el manejo de
multiples hilos y facilitando su manejo.
14

OpenMp permite una facilidad en el desarrollo de su


codigo, gracias a su simplicidad, mostrando un codigo no
tan distinto al presentado de manera secuencial.

0.7. Bibliografa
1. Calculo de pi tomado de: https://es.wikipedia.
org/wiki/Serie_de_Leibniz

2. Conceptos de CUDA tomado de: http://www.nvidia.


es/object/cuda-parallel-computing-es.html

3. Funciones de posix http://pubs.opengroup.org/


onlinepubs/7908799/xsh/pthread_join.html

4. Introduccion a OpenMP tomado de: https://sites.


google.com/a/unal.edu.co/computacion-paralela/

También podría gustarte