• Nenhum resultado encontrado

5.4 Detalhes de Implementa¸c˜ ao

5.4.2 Execu¸c˜ ao dos Kernels CUDA

5.4.2.2 Tratando Tipos Compostos

J´a vimos anteriormente que as threads criadas para a execu¸c˜ao dos kernels na GPU, realizam seus processamentos baseadas nos chamados elementos-base do multicon- junto. Vimos tamb´em, que a dinˆamica b´asica do processamento em cada thread consiste em realizar as combina¸c˜oes entre seu elemento-base e os demais elemen-

tos do multiconjunto, testando a condi¸c˜ao de rea¸c˜ao. Para que possa acessar um elemento qualquer do multiconjunto, incluindo o pr´oprio elemento-base, a thread precisa conhecer a posi¸c˜ao que ele ocupa no array de elementos, que como bem sabemos, ´e o formato no qual o multiconjunto ´e transformado para ser submetido ao processamento na GPU. Nos casos onde o multiconjunto ´e formado apenas por elementos do tipo simples, como por exemplo no programa "maximo.gm", este acesso pode ser feito de maneira simples e direta, uma vez que cada elemento do multicon- junto, ocupa apenas uma posi¸c˜ao no array, como podemos ver a seguir:

multiconjunto { 8, 2, 5, 9, 1, 4, 7, 3 }

↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓

array h 8, 2, 5, 9, 1, 4, 7, 3 i

Nesse caso, uma indexa¸c˜ao direta resolveria o problema do acesso, uma vez que temos oito elementos no multiconjunto e tamb´em oito posi¸c˜oes no array. Supondo que o elemento-base de uma thread fosse o quarto elemento do multiconjunto, de valor "9", bastaria acessar a quarta posi¸c˜ao do array (de ´ındice = 3), para obter tal valor, lembrando que a indexa¸c˜ao no array inicia em zero.

Todavia, este comportamento n˜ao se repete para os casos nos quais o multicon- junto ´e formado por elementos de tipos compostos (tuplas) ou por uma mistura de elementos simples e compostos. Nessas situa¸c˜oes, o n´umero de elementos presentes no multiconjunto ´e necessariamente menor que o n´umero de posi¸c˜oes no array re- sultante, visto que as tuplas s˜ao consideradas como um ´unico elemento no multicon- junto, mas ocupam mais de uma posi¸c˜ao quando s˜ao representadas sequencialmente na forma de array. Esta caracter´ıstica adiciona um n´ıvel de indire¸c˜ao na tarefa das threads de acessar corretamente os elementos, tornando-a mais dispendiosa, pois n˜ao h´a mais uma rela¸c˜ao direta de indexa¸c˜ao entre a posi¸c˜ao do elemento no multi- conjunto e a posi¸c˜ao no array. Desta forma, para que as threads pudessem realizar o devido acesso aos elementos nesta classe de multiconjuntos, foram criados dois arrays auxiliares, um chamado de offset e o outro de elem-size. A partir de agora, usaremos o termo bag-array, para nos referirmos ao array utilizado para representar o multiconjunto, a fim de evitar confus˜oes.

O array offset serve para armazenar os ´ındices do bag-array no qual um de- terminado elemento se encontra, ao passo que o array elem-size, ´e utilizado para guardar os tamanhos dos elementos compostos presentes no multiconjunto. Ambos possuem tamanho igual ao n´umero de elementos existentes no multiconjunto, os quais, ainda que sejam do tipo composto, contam como apenas um elemento. Ou seja, a indexa¸c˜ao destes dois arrays, o offset e o elem-size, ´e realizada de maneira

Figura 5.8: Exemplo de multiconjunto misto e dos arrays associados para o correto acesso aos seus elementos.

direta de acordo com a posi¸c˜ao do elemento no multiconjunto. Em outras palavras, uma thread que deseje acessar um elemento do bag-array, deve primeiro realizar uma indexa¸c˜ao direta no array offset, para obter a posi¸c˜ao correta de armazenamento, e depois, deve realizar outra indexa¸c˜ao direta, desta vez no array elem-size, para descobrir qual o tamanho do elemento que est´a sendo acessado, podendo assim, ob- ter os sucessivos valores em posi¸c˜ao e quantidade correta, para os casos de tipos compostos. A Figura 5.8 nos demonstra um exemplo de multiconjunto misto, e os arrays usados para sua representa¸c˜ao principal (bag-array), e para permitir o acesso de maneira correta (offset e elem-size).

Suponhamos que uma thread quisesse acessar o quarto elemento (´ındice = 3) do multiconjunto mostrado, que ´e a tupla "[4,5]". O primeiro passo seria realizar uma indexa¸c˜ao direta no ´ındice trˆes do array offset, e armazenar este valor em uma vari´avel para uso futuro, chamemos-na de "idx":

idx = offset[3]; ↓

idx = 6;

Depois, a thread deveria da mesma forma, indexar diretamente o array elem-size, obtendo o tamanho ("tam") do elemento sendo buscado:

elem-size h 1, 2, 3, 2 , 1, 3 i

tam = elem-size[3]; ↓

tam = 2;

Finalmente, ela poderia realizar o acesso ao elemento pretendido no bag-array, utilizando o ´ındice "idx=6" para index´a-lo, e sabendo que o elemento possui tama- nho "tam=2", ou seja, dois valores discretos para serem lidos:

bag-array h 2, 1, 1, 3, 3, 3, 4, 5, 7, 8, 8, 8 i valor_1 = bag-array[idx]; valor_2 = bag-array[idx+1]; ↓ valor_1 = bag-array[6]; valor_2 = bag-array[7]; ↓ valor_1 = 4; valor_2 = 5;

Na implementa¸c˜ao realizada, al´em das estruturas mencionadas, a representa¸c˜ao do multiconjunto no formato de array utilizou algumas outras vari´aveis ´uteis para seu funcionamento, como por exemplo, um array mantendo os ´ındices dos elementos-base. A seguir podemos ver a struct em linguagem C que foi criada e utilizada para manter as informa¸c˜oes do multiconjunto:

/* Run-Time bag-array, para processamento na GPU */

struct rt_bag_array {

int size; //n´umero de valores discretos no array

int *data; //valores discretos do array

int reaction_size; //n´umero de elementos em uma rea¸c~ao

int reaction_num_vals; //n´umero de valores discretos em uma rea¸c~ao

int num_elements; //n´umero de elementos no array

int *offset; //array de offsets dos elementos

int *elem_size; //array de tamanho dos elementos

int num_base; //n´umero de elementos-base no array

int *ind_base; //array de ´ındices dos elementos-base };