Generación del cubo de datos empleando paralelismo de

Transcripción

Generación del cubo de datos empleando paralelismo de
Instituto Politécnico Nacional
Centro de Investigación en
Computación
Generación del cubo de datos empleando
paralelismo de GPUs y CPUs multinúcleo
T E S I S
Que para obtener el grado de:
Maestro en Ciencias de la Computación
P R E S E N T A:
Mario Alfonso Torres Rivera
Directores:
Dr. Gilberto Lorenzo Martı́nez Luna
Dr. Adolfo Guzmán Arenas
2013
SIP-14 bis
INSTITUTO
POLITÉCNICO
SECRETARíA
DE INVESTIGACiÓN
NACIONAL
Y POSGRADO
ACTA DE REVISI6N DE TESIS
En la Ciudad de
noviembre
de
México, D.F.
siendo las
10:00
horas del día
21
del mes de
2Q13 se reunieron los miembros de la Comisión Revisora de la Tesis, designada
por el Colegio de Profesores de Estudios de Posgrado e Investigación del:
Centro de Investigación en Computación
para examinar la tesis titulada:
"Generación
del cubo de datos empleando
paralelismo
de GPUs y CPUs multinúcleo"
Presentada por el alumno:
TORRES
RIVERA
Apellido paterno
MARIO ALFONSO
Apellido materno
Con registro:
aspirante de: MAESTRíA EN CIENCIAS
'----""---L-..."--l.....-"--"-=--L-=--L-=--L-...=----.J
DE LA COMPUTACiÓN
Después de intercambiar opiniones los miembros de la Comisión manifestaron APROBAR LA
TESIS, en virtud de que satisface los requisitos señalados por las disposiciones reglamentarias
vigentes.
LA COMISiÓN REVISORA
Directores de Tesis
Dr. Ricardo Barrón Fernández
M. en
Dr. M~rco Antonio Ramírez Salinas
INSTITUTO POLITÉCNICO NACIONAL
SECRETARIA DE INVESTIGACIÓN Y POSGRADO
CARTA CESIÓN DE DERECHOS
En la Ciudad de México el dı́a 26 del mes Noviembre del año 2013 , el (la)
que suscribe Torres Rivera Mario Alfonso alumno (a) del Programa de Maestrı́a en
Ciencias de la Computación con número de registro B110843 , adscrito al Centro de
Investigación en Computación , manifiesta que es autor (a) intelectual del presente trabajo de Tesis bajo la dirección de Dr. Gilberto Lorenzo Martı́nez Luna y Dr. Adolfo
Guzmán Arenas y cede los derechos del trabajo intitulado Generación del cubo de
datos usando paralelismo de GPUs y CPUs multinúcleo , al Instituto Politécnico Nacional para su difusión, con fines académicos y de investigación.
Los usuarios de la información no deben reproducir el contenido textual, gráficas o
datos del trabajo sin el permiso expreso del autor y/o director del trabajo. Este puede
ser obtenido escribiendo a la siguiente dirección Av. Juan de Dios Bátiz, Esq. Miguel
Othón de Mendizábal, Col. Nueva Industrial Vallejo, Delegación Gustavo A. Madero,
C.P 07738, México D.F. Si el permiso se otorga, el usuario deberá dar el agradecimiento correspondiente y citar la fuente del mismo.
Mario Alfonso Torres Rivera
Resumen
La generación de cubos de datos de manera eficiente es un problema central en los almacenes
de datos y el procesamiento analı́tico en lı́nea. Es un proceso que puede implicar la ejecución
de gran número de operaciones aritméticas, además de consumir bastante tiempo cuando se
realiza a partir de datos de gran volumen. Para una relación R con n atributos o dimensiones
más un atributo de medida M , R(A1 , A2 , ..., An , M ), el problema básico del cálculo del cubo
de datos implica la agregación de R para construir 2n grupos de tuplas respecto a toda posible
combinación de las n dimensiones (i.e., el conjunto potencia de las n dimensiones de R), a cada
uno de estos grupos de tuplas se le llama cuboide. Dicho problema ha sido investigado y se han
propuesto estrategias para resolverlo, sin embargo, hasta ahora la mayorı́a de los algoritmos no
consideran las ventajas del paralelismo y las recientes arquitecturas de CPUs y GPUs.
En este trabajo se presenta el diseño de un conjunto de operaciones paralelas llamadas
primitivas que aprovechan el paralelismo proporcionado por los modelos recientes de GPUs y
CPUs multinúcleo. Las primitivas facilitan la generación de cubos de datos llevando a cabo
rutinas de ordenamiento, partición y agregación. La implementación del software para GPU de
este trabajo se realizó mediante la plataforma de cómputo en paralelo conocida como CUDA del
fabricante de procesadores gráficos NVIDIA y para implementar el paralelismo en procesadores
multinúcleo se utilizaron hilos POSIX.
Posteriormente, se introducen tres métodos paralelos para generación de cubos de datos
completos y de tipo iceberg. Además de las primitivas previamente diseñadas, estos métodos
utilizan hilos POSIX con el fin de explotar el paralelismo de CPUs multinúcleo en la construcción
simultánea de varios cuboides, i.e., todos los cuboides distribuyen en grupos y posteriormente
los cuboides de cada grupo se generan en paralelo. Se utiliza almacenamiento en memoria lineal
a través de arreglos de una dimensión para almacenar tuplas en memoria principal, evitando
costos relacionados con la construcción de estructuras de datos más complejas. Ası́ mismo, se
utilizan algunas estrategias conocidas en la literatura a fin de agilizar la generación del cubo
de datos, sin embargo, a diferencia de los trabajos previos, los métodos presentados en esta
tesis constan de un paralelismo de grano fino que se obtiene a través del uso de las primitivas
paralelas.
4
Abstract
Efficient data cube computing is a core problem in data warehousing and online analitical
processing fields. This is a process that may involve very large amount of tuple group summarization over big data. For a given relation R with n dimensions and a measure attribute M ,
R(A1 , A2 , ..., An , M ), the basic data cube generation problem involves the aggregation of R for
the construction of 2n tuple groups on every possible combination of the n dimensions (i.e., the
power set of the n dimensions of R), each of this groups is called a cuboid. This problem has been
researched extensively, however, most of algorithms have been proposed without considering the
advantages of the modern CPU and GPU architectures.
This work presents the design and implementation of a set of parallel operations called
primitives which take advantage of the modern GPU and multicore CPU parallelism. Primitives
help to generate data cubes conducting routines such as sort, partition, and aggregate. The
GPU software was implemented using CUDA, a parallel computing platform introduced by the
graphics processor manufacturer NVIDIA, on the other hand, multicore CPU parallelism was
implemented using POSIX threads.
Subsequently, we introduce tree parallel methods for efficient generation of full and iceberg data cubes. Besides the previously mentioned parallel primitives, this methods use POSIX
threads to exploit the multicore CPU parallelism in the simultaneous construction of several
cuboids, i.e., all cuboids are distributed into groups and then cuboids in each group are constructed in parallel. Linear memory storage is used to keep tuples in main memory, avoiding
additional costs related to building more complex data structures. Likewise, we use some well
known strategies to accelerate the data cube computing process, but unlike previous work, our
methods feature fine grained parallelism provided by parallel primitives.
5
Agradecimientos
Este trabajo fue realizado bajo la dirección de los profesores Dr. Gilberto Lorenzo Martı́nez
Luna y Dr. Adolfo Guzmán Arenas. Quiero agradecer ampliamente su apoyo, confianza y guı́a
a lo largo del desarrollo de la tesis. Agradezco también a los profesores, M. en C. Alejandro
Botello Castillo, Dr. Marco Antonio Ramı́rez Salinas, Dr. José Giovanni Guzmán Lugo y Dr.
Ricardo Barrón Fernández, por sus valiosos comentarios para el enriquecimiento de este trabajo.
A mis compañeros y amigos del CIC, Rodolfo Vilchis, Eliezer Alcázar, Edgar Garcı́a. Siempre recordaré los momentos que compartimos.
Finalmente, agradezco a mi familia: a mis padres, Esteban y Lucı́a, a quienes jamás terminaré de corresponder por el gran amor, comprensión y apoyo que me han brindado en cada
momento de mi vida; a mi hermana Lucı́a Isabel, con mucho cariño; a la familia Torres - Rodriguez, por todo su afecto y apoyo incondicional.
6
Índice general
Agradecimientos
6
1. Introducción
22
1.1. Motivación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
23
1.2. Objetivo general
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
1.2.1. Objetivos particulares . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
1.3. Justificación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
24
1.4. Alcances y limitaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
25
1.5. Aportaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
26
1.6. Organización de la tesis
26
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2. Fundamentos y trabajos previos
27
2.1. El procesamiento analı́tico en lı́nea y los cubos de datos . . . . . . . . . . . . . .
28
2.1.1. Funciones de agregación . . . . . . . . . . . . . . . . . . . . . . . . . . .
32
2.1.2. Operadores GROUP BY y CUBE BY . . . . . . . . . . . . . . . . . . . .
33
2.1.3. Generación del cubo de datos . . . . . . . . . . . . . . . . . . . . . . . .
34
2.2. Paralelismo en bases de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . .
39
2.2.1. Paralelismo a gran escala . . . . . . . . . . . . . . . . . . . . . . . . . . .
40
2.2.2. Arquitecturas paralelas . . . . . . . . . . . . . . . . . . . . . . . . . . . .
42
2.3. Tecnologı́a multinúcleo y de muchos núcleos . . . . . . . . . . . . . . . . . . . .
44
2.3.1. Procesamiento paralelo en CPUs multinúcleo . . . . . . . . . . . . . . . .
45
2.3.2. Cómputo de propósito general en unidades procesamiento gráfico . . . .
47
2.4. Estado del arte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
50
2.4.1. Generación secuencial de cubos de datos . . . . . . . . . . . . . . . . . .
51
2.4.2. Generación de cubos de datos empleando paralelismo de clusters de PCs
53
2.4.3. Estrategias de uso de memoria caché en estructuras de datos y generación
de cubos de datos empleando memoria caché . . . . . . . . . . . . . . . .
55
7
2.4.4. Generación en paralelo de cubos de datos usando tecnologı́a multinúcleo
y de GPUs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
2.4.5. Similaridades entre trabajos previos y los métodos propuestos . . . . . .
3. Arquitectura de la solución
3.1. Configuración del sistema de cómputo . . . . . . . . . . .
3.2. Primitivas paralelas . . . . . . . . . . . . . . . . . . . . .
3.2.1. Formato de almacenamiento en memoria lineal . .
3.2.2. Recolección . . . . . . . . . . . . . . . . . . . . .
3.2.3. Ordenamiento . . . . . . . . . . . . . . . . . . . .
3.2.4. Proceso de ordenamiento de tuplas . . . . . . . .
3.2.5. ParticiónLocal y particiónCuboide . . . . . . . .
3.2.6. Reducción y reducciónSegmentada . . . . . . . .
3.2.7. Construcción de cuboides . . . . . . . . . . . . .
3.3. Métodos paralelos de generación de cubos de datos . . .
3.3.1. Método MCBUC . . . . . . . . . . . . . . . . . .
3.3.2. Método SPCube . . . . . . . . . . . . . . . . . .
3.3.3. Método GPUgenCube . . . . . . . . . . . . . . .
3.3.4. Comparativa de los métodos para cubos de datos
4. Pruebas y resultados
4.1. Equipo de pruebas . . . . . . . . . . . .
4.2. Conjunto de datos de prueba . . . . . . .
4.3. Desempeño del ordenamiento . . . . . .
4.4. Desempeño de los algoritmos de cubos de
4.4.1. Cubo completo . . . . . . . . . .
4.4.2. Cubo iceberg . . . . . . . . . . .
4.4.3. Sesgo . . . . . . . . . . . . . . . .
4.4.4. Funciones de agregación . . . . .
4.4.5. Observaciones . . . . . . . . . . .
. . . .
. . . .
. . . .
datos
. . . .
. . . .
. . . .
. . . .
. . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
56
57
.
.
.
.
.
.
.
.
.
.
.
.
.
.
59
60
62
63
65
67
75
80
84
88
90
91
97
103
108
.
.
.
.
.
.
.
.
.
109
110
110
111
112
113
116
119
121
130
5. Conclusiones
132
5.1. Trabajo futuro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Referencias
135
8
Índice de figuras
2.1. Tabla cruzada de la relación ventas sobre los atributos Artı́culo y Fabricante. En
este caso, el atributo capacidad tiene asignado el valor especial ALL . . . . . . .
29
2.2. Conceptualización de un cubo de datos de tres dimensiones. El cubo de datos
muestra los totales de ventas para una tienda de dispositivos electrónicos dispuestos de acuerdo a toda combinación de Artı́culo, Fabricante y Capacidad. Por
ejemplo, se puede ver que se vendieron 20 unidades de estado solido del fabricante
F1, 7 de 128GB, 12 de 32GB y 1 de 16GB. . . . . . . . . . . . . . . . . . . . . .
31
2.3. Operador relacional GROUP BY: Particiona una tabla en grupos. Cada grupo
es agregado por una función. La función de agregación sumariza alguna columna
de grupos regresando un valor por cada grupo. . . . . . . . . . . . . . . . . . . .
33
2.4. Cubo de datos sobre una relación de ventas de automóviles. . . . . . . . . . . .
34
2.5. Conceptualización de un cubo iceberg de tres dimensiones con umbral de soporte
mı́nimo SUM(ventas)≥10. El cubo iceberg muestra los totales de ventas para
una tienda de dispositivos electrónicos dispuestos de acuerdo a toda combinación
de Artı́culo, Fabricante y Capacidad. Se puede ver que las vistas solo incluyen
valores agregados que satisfacen el umbral de soporte mı́nimo. . . . . . . . . . .
35
2.6. Estructura de lattice para un cubo de datos de cuatro dimensiones. . . . . . . .
36
2.7. Ejemplo de árbol de procesamiento presente en algoritmos descendentes. . . . .
37
2.8. Ejemplo de árbol de procesamiento presente en algoritmos ascendentes. . . . . .
37
2.9. Representación conceptual de la arquitectura memoria compartida. Cualquier
procesador tiene acceso a cualquier módulo de memoria o unidad de disco a
través de una conexión rápida. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
43
2.10. Representación de los diseños de procesadores multinúcleo (CPU) y de muchos
núcleos (GPU) respectivamente. Los procesadores multinúcleo normalmente tienen entre dos y ocho núcleos, orientados a la aceleración de procesos secuenciales. En contraste, las GPUs están enfocadas a problemas paralelos, contando con
cientos de núcleos menos potentes que los de una CPU multinúcleo. . . . . . . .
44
9
2.11. Representación conceptual de un proceso de UNIX. Un proceso de UNIX cuenta
con recursos que permiten la ejecución de un programa como una pila, texto de
programa y datos usados por el programa. . . . . . . . . . . . . . . . . . . . . .
45
2.12. Representación conceptual de proceso de UNIX con dos hilos. Los hilos duplican
los recursos que les permiten existir como código ejecutable dentro de un proceso
de UNIX. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
46
2.13. Organización tı́pica de una GPU conformada por 30 multiprocesadores, cada uno
con 8 ALUs SIMD y una espacio de memoria local compartida. . . . . . . . . . .
48
2.14. Jerarquı́a de hilos, bloques y mallas en CUDA con sus respectivos espacios de
memoria. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
49
3.1. Representación de la jerarquı́a de memoria en una GPU Fermi [32] del fabricante
NVIDIA. La memoria compartida y caché L1 se encuentran en el mismo nivel ya
que la latencia de acceso a ellas es equivalente. . . . . . . . . . . . . . . . . . . .
60
3.2. Conversión de cuatro tuplas en formato tabular a un formato vectorial. Cada
columna es escrita a una sección del vector resultante, la primera sección contiene
a los elementos de la primera columna, la segunda sección a los de la segunda y
ası́ sucesivamente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
64
3.3. Representación la primitiva recolección sobre un vector de ocho elementos. El
valor de la posición 0 del mapa (es el 1, de izquierda a derecha) indica que se
accederá la posición 1 del vector de valores que este caso contiene el valor 21 el
cual irá en la posición 0 de la salida; el valor de la posición 1 del mapa (es el
2) indica el acceso al valor 9 que irá en la posición 1 de la salida; el valor de la
posición 2 del mapa (es el 0) indica el acceso al valor 10 que irá en la posición 2
de la salida; el proceso continua hasta acceder los 8 elementos de la entrada. . .
66
3.4. Ejemplo de ordenamiento por clave de un vector de seis elementos. El vector de
claves sirve como referencia para ordenar un vector de valores. . . . . . . . . . .
68
3.5. Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 1
de 3. La ilustración muestra el ordenamiento de la lista de números respecto al
primer dı́gito decimal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
71
3.6. Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 2
de 3. La ilustración muestra el ordenamiento de la lista de números respecto al
segundo dı́gito decimal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
3.7. Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 3
de 3. La ilustración muestra el ordenamiento de la lista de números respecto al
tercer dı́gito decimal. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
72
10
3.8. Conceptualización de los histogramas del radix sort paralelo. Cada bloque de
hilos de GPU consta de un histograma para ordenar una sección de la secuencia
de entrada. En cada pasada del radix sort se realiza una operación de suma
de prefijos global a través de todos los histogramas para determinar la posición
global de los elementos ordenados por cada bloque de hilos en el arreglo de salida. 74
3.9. Ordenamiento de cuatro tuplas respecto a la combinación de atributos ABC. Las
tuplas a ordenar son (2, 2, 1), (1, 1, 1), (3, 1, 2) y (5, 1, 1). Esto se muestra en
el vector superior. El resultado de ordenar las tuplas a la combinación ABC es:
(1, 1, 1), (2, 2, 1), (3, 1, 2) y (5, 1, 1). Esto se muestra en el vector inferior. . . .
76
3.10. Generación de un mapa para ordenar las tuplas de la Figura 3.9 respecto a la
combinación ABC. El mapa es inicialmente una secuencia ascendente de enteros
cuyos valores van permutando debido a una serie de ordenamientos por clave.
Las claves son los valores de un cierto atributo de los datos en bruto y se inicia
por ordenar respecto al atributo menos significativo, en este caso C. La versión
final del mapa permite usar a la recolección para re-acomodar los valores de
cada sección del vector de tuplas (datos en bruto) correspondiente a un atributo,
dejando las tuplas ordenadas respecto a la combinación que se consideró, en este
caso ABC. Véase la Figura 3.11. . . . . . . . . . . . . . . . . . . . . . . . . . .
77
3.11. Fase final del ordenamiento de las tuplas en la Figura 3.9. Se usa el mapa de la
Figura 3.10 para aplicar la recolección a cada sección del vector de tuplas que
corresponde a un atributo de los datos. Esta última fase deja a las tuplas de la
Figura 3.9 ordenadas respecto a la combinación ABC. . . . . . . . . . . . . . . .
78
3.12. Particionamiento de una tabla con cinco tuplas respecto a la combinación de
atributos ABC. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
80
3.13. Representación del proceso de partición de un grupo de tuplas en formato de
vector respecto a la combinación de atributos ABC. . . . . . . . . . . . . . . . .
82
3.14. Ejemplo del proceso de partición de un vector con cinco tuplas respecto a la
combinación de atributos ABC. ParticiónLocal se encarga de lanzar un hilo de
CPU para particionar cada atributo que se requiera (en este caso A, B y C). ParticiónCuboide realiza la unión con los marcadores producidos por particiónLocal
(en este caso, marcadores de A, B y C). El resultado de particiónCuboide es un
conjunto de marcadores que permite la generación de un cuboide o vista del cubo
(en este ejemplo son para el cuboide ABC). . . . . . . . . . . . . . . . . . . . .
83
3.15. Reducción de un vector de ocho elementos. . . . . . . . . . . . . . . . . . . . . .
84
11
3.16. Reducción de un vector en dos fases. En la primera fase, un conjunto de bloques
de hilos reduce varias secciones de un arreglo o vector de valores, produciendo
resultados parciales. La segunda fase obtiene el resultado final de la reducción
usando un solo bloque de hilos sobre los resultados parciales de la primera fase. .
86
3.17. ReducciónSegmentada de un vector. Los elementos del vector de la ilustración
están divididos en tres segmentos y la operación realizada es una sumatoria. La
reducción de cada segmento del vector produce a un escalar. . . . . . . . . . . .
87
3.18. Representación del proceso de construcción de un cuboide de tres dimensiones
utilizando la función SUM. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
89
3.19. Árbol de procesamiento del algoritmo BUC [3] para un cubo de datos de cuatro
dimensiones. Los números indican el orden de cálculo para los cuboides. . . . . .
91
3.20. Particionamiento usado en los métodos BUC [3] y MCBUC de un conjunto de
datos de cuatro dimensiones. Los ai son valores del atributo A, los valores bi
corresponden al atributo B y ası́ sucesivamente. . . . . . . . . . . . . . . . . . .
92
3.21. Ejemplo de poda Apriori. Los grupos que no cumplen con la cláusula iceberg son
ignorados al construir un cuboide. Para este ejemplo, se ignoran los grupos con
menos de dos tuplas. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
93
3.22. Esquema conceptual de la ejecución del método MCBUC para la generación de un
cubo de datos de dos dimensiones. Las flechas muestran el flujo de procesamiento
recursivo de este método. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
96
3.23. Esquema del método SPCube. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
97
3.24. Lattice para un cubo de datos de cuatro dimensiones. Las flechas indican rutas
potenciales de cálculo para el método SPCube. . . . . . . . . . . . . . . . . . . .
98
3.25. Árbol de procesamiento generado por el método SPCube. Los números a la derecha de cada combinación de atributos indican la cantidad de tuplas del cuboide,
M y K indican millones y miles respectivamente. Estas cantidades permiten realizar una selección que producirá el menor costo al generar vistas del cubo a partir
de otras más detalladas (ancestros en la jerarquı́a de lattice). . . . . . . . . . . .
99
3.26. Esquema de ejecución del método SPCube para un cubo de tres dimensiones. Los
cuboides son generados a partir de otros más detallados en lugar de los datos en
bruto. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
3.27. Esquema del método GPUgenCube. . . . . . . . . . . . . . . . . . . . . . . . . . 103
3.28. Lattice para un cubo de datos de cuatro dimensiones. . . . . . . . . . . . . . . . 105
12
3.29. Esquema de ejecución del método GPUgenCube para un cubo de tres dimensiones. Las tareas son ejecutadas una a una por GPUgenCube. Los cuboides de una
cierta tarea son generados y escritos simultáneamente a memoria secundaria por
un hilo de CPU. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.1. Desempeño de algoritmos de ordenamiento sobre conjuntos de datos distribuidos
aleatoriamente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.2. Desempeño de algoritmos de ordenamiento sobre conjuntos de enteros en orden
decreciente. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.3. Generación del cubo completo, dimensiones = 7, cardinalidad = 10, sesgo = 0.
113
4.4. Generación del cubo completo, tuplas = 10m, dimensiones = 7, sesgo = 0. . . . 114
4.5. Generación del cubo completo, tuplas = 10m, cardinalidad = 20, sesgo = 0. . . 114
4.6. Generación del cubo completo, dimensiones = 8, cardinalidad = 10, sesgo = 0.
115
4.7. Generación del cubo completo, tuplas = 10m, dimensiones = 7, sesgo = 0, sin
considerar el tiempo de escritura a disco. . . . . . . . . . . . . . . . . . . . . . . 115
4.8. Generación del cubo completo, tuplas = 10m, cardinalidad = 20, sesgo = 0, sin
considerar el tiempo de escritura a disco. . . . . . . . . . . . . . . . . . . . . . . 116
4.9. Generación del cubo iceberg, tuplas = 10m, dimensiones = 7, sesgo = 0,
minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
4.10. Generación del cubo iceberg, tuplas = 10m, dimensiones = 8, sesgo = 0,
minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
4.11. Generación del cubo iceberg, tuplas = 10m, cardinalidad = 20, dimensiones = 7,
sesgo = 0. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.12. Generación del cubo iceberg, tuplas = 10m, cardinalidad = 20, dimensiones = 8,
sesgo = 0. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.13. Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas =
10m, dimensiones = 7, cardinalidad = 20, minsup = 100. . . . . . . . . . . . . 119
4.14. Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas =
10m, dimensiones = 7, cardinalidad = 40, minsup = 50. . . . . . . . . . . . . . 120
4.15. Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas =
10m, dimensiones = 7, cardinalidad = 100, minsup = 50. . . . . . . . . . . . . 120
4.16. Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas =
10m, dimensiones = 8, cardinalidad = 100, minsup = 10. . . . . . . . . . . . . 121
4.17. SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
4.18. SPCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los
datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 100. . . . 122
13
4.19. SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
4.20. SPCube: Generación del cubo iceberg variando en el nivel de sesgo (Zipf) de los
datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 20. . . . . 123
4.21. SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
4.22. SPCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los
datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 20. . . . . 124
4.23. GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
4.24. GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de
los datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 100. . 125
4.25. GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 20. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
4.26. GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de
los datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 20. . . 126
4.27. GPUgenCube: Generación del cubo iceberg, AVG, tuplas = 10m, sesgo = 1,
cardinalidad = 20, minsup = 20. . . . . . . . . . . . . . . . . . . . . . . . . . . 127
4.28. GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de
los datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 20. . . 127
4.29. GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 100. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
4.30. GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de
los datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 100. . 128
4.31. GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad =
20, minsup = 20. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
4.32. GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de
los datos, tuplas = 10m, dimensiones = 7, cardinalidad = 20, minsup = 20. . . 129
14
Índice de tablas
2.1. Representación tabular de los datos en la Figura 2.1. . . . . . . . . . . . . . . .
30
2.2. Métodos previos de generación de cubos de datos, estrategias empleadas. Las
similaridades con los métodos MCBUC, SPCube y GPUgenCube propuestos en
esta tesis se resaltan en la última columna de la derecha. . . . . . . . . . . . . .
58
3.1. Variables comúnmente usadas como configuración para una función kernel de
CUDA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
61
3.2. Tuplas en formato tabular. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
63
3.3. Tuplas de la Tabla 3.2 en formato de vector. . . . . . . . . . . . . . . . . . . . .
63
3.4. Prototipo de la primitiva recolección. La primitiva recibe dos arreglos de enteros,
Re y mapa, de tamaño n como entrada, su función es acceder las posiciones de
Re de acuerdo con los valores en mapa, i.e., Re [mapa[i]] donde i va de 1 a n. . .
65
3.5. Prototipo de la primitiva ordenamiento. Re y claves son arreglos de una dimensión. Los valores en claves son utilizados como referencia para ordenar los valores
de Re . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
67
3.6. Prototipo de la primitiva particiónLocal. Su función es recorrer en Re las secciones
correspondientes a los valores de cada atributo de la combinación respecto a
la cual se desea particiónar, obteniendo un conjunto de marcadores por cada
atributo. Los marcadores son las posiciones de Re donde se registró un cambio
de valor. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
81
3.7. Prototipo de la primitiva particiónCuboide. La función de esta primitiva es realizar una operación de unión con los conjuntos de marcadores de cada atributo de
la combinación respecto a la que se va a particionar. Los marcadores se encuentran almacenados en un arreglo Re que para esta fase puede contener marcadores
para otros atributo no incluidos en la combinación. El resultado es un arreglo de
marcadores Rs tomando en cuenta solo a los atributos de la combinación. . . . .
81
15
3.8. Prototipo de la primitiva reducción. La función de esta primitiva es evaluar los
elementos de un arreglo de entrada Re mediante un operador binario asociativo
⊕, produciendo un solo elemento Rs como salida. . . . . . . . . . . . . . . . . . 85
3.9. Prototipo de la primitiva reducciónSegmentada. La función de esta primitiva
es evaluar varios segmentos de un arreglo de entrada Re mediante un operador
binario asociativo ⊕, produciendo un elemento como salida por cada segmento,
es decir, un arreglo de salida Rs . Los segmentos se encuentran delimitados por
valores continuos en un segundo arreglo (claves) del mismo tamaño del arreglo a
evaluar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.10. Asignación de tareas en el método MCBUC para un cubo de datos de cuatro
dimensiones. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3.11. Asignación de tareas en el método SPCube para un cubo de datos de cuatro
dimensiones. Los cuboides son agrupados de acuerdo al número de atributos. . . 98
3.12. Asignación de tareas en el método GPUgenCube para un cubo de datos de cuatro
dimensiones. Las tareas son procesadas una a una, empleando paralelismo de
grano fino a través de las primitivas paralelas y generando tuplas en paralelo
para los cuboides de una cierta tarea en paralelo. . . . . . . . . . . . . . . . . . 105
3.13. Resumen de caracterı́sticas para los métodos SPCube, MCBUC y GPUgenCube. 108
3.14. Funciones de agregación en los métodos MCBUC, SPCube y GPUgenCube. Las
marcas de verificación indican las funciones implementadas para cada método. . 108
4.1. Especificación técnica del sistema de pruebas. . . . . . . . . . . . . . . . . . . . 110
16
Índice de algoritmos
1.
2.
3.
4.
5.
6.
7.
8.
conteoPalabras . . . . . .
ordenamiento por cuentas
ordenarTuplas . . . . . . .
BottomUpCube . . . . . .
MCBUC . . . . . . . . . .
padreMenorCosto . . . . .
SPCube . . . . . . . . . .
GPUgenCube . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
. 41
. 70
. 79
. 94
. 95
. 100
. 101
. 106
17
Definiciones
A continuación se definen varios términos comúnmente utilizados esta tesis.
Cardinalidad: En el contexto de bases de datos, cardinalidad se refiere a la unicidad de
los valores contenidos en una columna o atributo de una tabla. Alta cardinalidad significa
que una columna contiene alto porcentaje de valores totalmente únicos. Baja cardinalidad
implica que la columna contiene alto porcentaje de valores repetidos. La cardinalidad de
un atributo A se simboliza mediante |A|.
Claves: Es una agrupación de valores enteros organizada mediante un arreglo unidimensional, se utiliza ya sea como referencia para ordenar un segundo grupo de valores o bien
para particionar otro arreglo de igual longitud.
Cluster : Grupo (decenas o cientos) de computadoras conectadas a través de una red para
trabajar en conjunto, de manera que pueden verse como un solo sistema.
Cubo completo: Se refiere a la generación de todas las vistas del cubo de datos.
Cubo iceberg: Se refiere a la generación de las vistas o la parte de las vistas del cubo de
datos que cumplen con un umbral de soporte mı́nimo previamente especificado.
Cuboide: Se utiliza para denotar una vista de un cubo de datos.
Formato tabular: Se refiere a la representación de una cierta información en forma de tabla
con filas y columnas.
Datos en bruto: Se refiere a la totalidad de las tuplas a partir de las cuales se está generando
un cubo de datos.
Función de agregación: Es una función que toma como parámetro un conjunto de valores
y devuelve uno solo como resultado. Una función de agregación pueden ser clasificada
como distributiva, algebraica u holı́sticas. SUM, MAX, MIN y COUNT son ejemplos de
funciones distributivas, AVG es algebraica y funciones como la mediana y el rango son
holı́sticas.
18
Núcleo: Es una unidad central de procesamiento independiente integrada dentro de un
componente de cómputo o multiprocesador.
Mapa: En este trabajo se utiliza para denotar un conjunto de ı́ndices utilizados para
acceder posiciones de un arreglo. Fı́sicamente el mapa es un arreglo de valores enteros.
Marcador: Es un valor que define una posición o ı́ndice de un arreglo.
Mosaico: Se refiere a un trozo o sección de un arreglo de una dimensión.
Relación: Es un conjunto de tuplas que tienen los mismos atributos. En el contexto de
bases de datos, una relación usualmente se describe como una tabla, la cual está organizada
en renglones y columnas.
Relación base: En este trabajo se refiere a la relación a partir de la cual se está generando
un cubo de datos.
Suma de prefijos: Es una suma acumulativa, i.e., una secuencia de sumas parciales de
una secuencia dada. Por ejemplo, las sumas acumulativas de la secuencia {a, b, c, ...} son
a, a + b, a + b + c, ....
Tabla cruzada: Se le conoce también como tabla dinámica, es una tabla donde los valores
de uno de los atributos forman las cabeceras de las filas, los valores de otro atributo
forman las cabeceras de las columnas y los valores de cada celda se obtienen mediante la
agregación de los valores de un conjunto de atributos.
Tarea: Denota un conjunto de cuboides que pueden agruparse en base a alguna caracterı́stica y calcularse simultáneamente.
Tupla: Es una lista ordenada de elementos. Por ejemplo, (a, b, c, d) denota una tupla con
4 elementos (4-tupla).
Vector: Se refiere a un simplemente a arreglo unidimensional.
Vista: En la generación de un cubo de datos de n dimensiones a partir de una relación R,
una vista es el resultado de la agregación de R respecto a una cierta combinación de las
dimensiones del cubo. Por ejemplo, considere un cubo de datos con dimensiones A y B, las
combinaciones posibles son {{}, A, AB}, entonces la vista {} corresponde a la agregación
total de las tuplas de R; la vista A corresponde a la agregación de R respecto a los grupos
formados por cada distinto valor ai de A; la vista AB corresponde a la agregación de R
respecto a los grupos formados por cada distinto par de valores (ai , bi ) donde ai ∈ A y
bi ∈ B.
19
Siglas y acrónimos
Las siguientes siglas y acrónimos son utilizadas con frecuencia en los capı́tulos de esta tesis.
ALU: Unidad aritmético - lógica (en inglés,Aritmetic Logic Unit). Es un circuito digital
que realiza operaciones aritméticas y lógicas. La ALU es un bloque fundamental en una
unidad central de procesamiento.
CPU: Unidad central de procesamiento (en inglés, Central Processing Unit). Es el componente principal de una computadora o cualquier otro dispositivo programable, se encarga de interpretar las instrucciones contenidas en los programas, realizando operaciones
aritméticas, lógicas y de entrada/salida.
CSV: Valores separados por coma (en inglés, Comma-Separated Values). Es un formato
comúnmente utilizado para representar información en formato tabular.
CUDA: Arquitectura de dispositivo de cómputo unificado (en inglés, Compute Unified
Device Architecture). Es una plataforma de cómputo en paralelo y modelo de programación
que utiliza GPUs como motores de cálculo.
GPU: Unidad de procesamiento gráfico (en inglés, Graphics Processing Unit). Es un circuito electrónico dedicado al procesamiento de gráficos u operaciones de coma flotante, su
función es aligerar la carga de trabajo de la unidad central de procesamiento en aplicaciones como los videojuegos. Las GPUs también son utilizados como motores de cómputo
de propósito general.
MPI: Interfaz de paso de mensajes (en inglés, Message Passing Interface). Es un sistema
portable y estandarizado de paso de mensajes diseñado para funcionar sobre una amplia
variedad de computadoras paralelas.
20
OLAP: Procesamiento analı́tico en lı́nea (en inglés, On-Line Analytical Processing). Es
una solución utilizada en el campo de la Inteligencia de Negocios, la cual consiste en
consultas a estructuras multidimensionales (o cubos de datos OLAP) que contienen datos
resumidos de grandes bases de datos o sistemas transaccionales.
PCI Express: Interconexión de componentes periféricos Express (en inglés, Peripheral
Component Interconnect Express). Es un bus serial de alta velocidad que permite conectar
dispositivos electrónicos con un sistema de cómputo.
POSIX: Interfaz portable de sistema operativo (en inglés, Portable Operating System Interface). Es una familia de estándares especificada por la organización IEEE para mantener
la compatibilidad entre sistemas operativos.
SIMD: Simple instrucción múltiples datos (en inglés, Single Instruction Multiple Data).
Es una clase de computadora paralela que realiza simultáneamente una misma operación
sobre diferentes conjuntos de datos.
SQL: Lenguaje estructurado de consulta (en inglés, Structurated Query Language). Es
un lenguaje de programación de propósito especial diseñado para la gestión de datos en
sistemas manejadores de base de datos relacionales.
21
Capı́tulo 1
Introducción
El cálculo de cubos de datos de manera eficiente es un problema central en los almacenes
de datos y el procesamiento analı́tico en lı́nea. Es un proceso que puede implicar la ejecución
de gran número de operaciones aritméticas, además de consumir bastante tiempo cuando se
realiza a partir de datos de gran volumen. Para una relación R con n atributos o dimensiones
más un atributo de medida M , R(A1 , A2 , ..., An , M ), el problema básico del cálculo del cubo
de datos implica la agregación de R para construir 2n grupos de tuplas respecto a toda posible
combinación de las n dimensiones (i.e., el conjunto potencia de las n dimensiones de R), a cada
uno de estos grupos de tuplas se le llama cuboide o vista del cubo. Cada cuboide corresponde
a un conjunto de tuplas donde uno o más de los elementos de cada tupla es un valor agregado
que se calculó a partir de una partición de la relación en base a la cual se generó el cubo de
datos [16]. Como una especialización al problema del cálculo del cubo de datos, el cubo iceberg
fue introducido en [3] y consiste en calcular la parte de los cuboides que cumple con un umbral
de soporte mı́nimo previamente especificado por el usuario. Si existe una condición iceberg, la
cual toda partición de la relación base debe cumplir, por ejemplo SUM(M) > 100, la tarea es
calcular un cubo iceberg, de lo contrario se calcula el cubo completo.
En la práctica el tamaño de los cubos de datos puede aumentar de manera exponencial
respecto al de la relación en base a la cual se generaron, ocupando gigabytes de espacio en
disco. El tamaño máximo de un cubo de datos de n dimensiones generado a partir de K tuplas
distintas es igual a (2n − 1)K + 1. En [8] por ejemplo, se construyó un cubo de datos de 8
dimensiones sobre un conjunto de 256 millones de tuplas, el resultado fue de aproximadamente
7 billones de tuplas (200 gigabytes). Es entonces poco probable que las plataformas con un
solo procesador puedan manejar el enorme tamaño de los mas recientes y futuros sistemas
de soporte a la toma de decisiones, para estos casos, el procesamiento en paralelo ofrece dos
ventajas básicas: mayor capacidad de cómputo a través de múltiples procesadores y mayor ancho
de banda mediante almacenamiento paralelo.
22
La generación de cubos de datos ha sido ampliamente investigada, sin embargo, hasta ahora
la mayorı́a de los algoritmos no consideran las ventajas del paralelismo y las recientes arquitecturas de cómputo. En la actualidad la mayorı́a de equipos de cómputo recientes constan
de procesadores que pueden ser utilizados para implementar el paralelismo, tal es el caso de
los CPUs multinúcleo y las unidades de procesamiento gráfico o GPUs. Los procesadores multinúcleo pueden ser programados para ejecutar simultáneamente un moderado número de tareas
utilizando varios procesadores integrados dentro de una sola unidad de cómputo. Es común encontrar chips de entre dos y ocho núcleos, donde cada núcleo es un procesador que implementa
el conjunto completo de instrucciones x86. Por otro lado, las GPUs son dispositivos que constan
de cientos de procesadores y que pueden ser usados para efectuar tareas masivamente paralelas. En este trabajo se proponen tres métodos paralelos para la generación de cubos de datos
completos y de tipo iceberg utilizando tecnologı́a de GPUs y CPUs multinúcleo.
1.1.
Motivación
A causa del abaratamiento de la tecnologı́a de almacenamiento y el incremento del ancho
de banda de las conexiones a Internet, es frecuente encontrar almacenes de datos cuyo tamaño
oscila en las centenas de terabytes, a los que regularmente se les añaden grandes volúmenes de
datos, tal crecimiento conduce a extremos la habilidad de sistemas con un solo procesador para
manejar dichas cargas. La tecnologı́a actual permite colocar hasta cierto lı́mite de transistores
en un solo chip, por lo que el desarrollo de procesadores cada vez más rápidos no representa
una opción factible para aumentar el desempeño de los sistemas de cómputo. Esta limitación
hizo necesaria la búsqueda de alternativas, dando como resultado al paralelismo.
Las GPUs y CPUs multinúcleo son dispositivos comunes en sistemas de cómputo recientes,
con gran capacidad de cálculo, mediano costo y moderado consumo energético que pueden ser
utilizadas para mejorar el tiempo de respuesta del procesamiento analı́tico en lı́nea en sistemas
de apoyo a la toma de decisiones ası́ como en la aceleración de tareas de minerı́a en almacenes
de datos extensos.
23
1.2.
Objetivo general
Diseño e implementación de métodos paralelos para generación de cubos de datos completos
y de tipo iceberg, aprovechando las ventajas del procesamiento en GPUs y CPUs multinúcleo.
1.2.1.
Objetivos particulares
Diseñar e implementar un conjunto de primitivas paralelas que permitan el manejo eficiente de datos aprovechando el paralelismo de GPUs y CPUs multinúcleo, ası́ como los
niveles de memoria de la GPU.
Diseñar e implementar métodos paralelos de generación de cubos de datos completos y de
tipo iceberg empleando tecnologı́a multihilo y primitivas paralelas.
Diseñar e Implementar funciones de agregación distributivas y algebraicas para los métodos de cubos de datos.
1.3.
Justificación
El cubo de datos es una estructura multidimensional con un alto porcentaje de valores
agregados, fundamental en algunas áreas que soportan la toma de decisiones, ya que permite
representar y analizar datos de acuerdo a una medida de interés. El cálculo eficiente de cubos
de datos ha sido uno de los puntos hacia donde la investigación en el área de bases de datos se
ha enfocado desde la introducción de los almacenes de datos y el OLAP. Sin embargo, hasta el
momento la mayorı́a de los algoritmos de cubos de datos han sido propuestos sin considerar las
ventajas de los procesadores multinúcleo y los recientes modelos de GPUs.
En la actualidad las GPUs y CPUs multinúcleo están presentes en la mayor parte de equipos
de cómputo, dotando a máquinas ordinarias de potentes procesadores paralelos con gran capacidad de cálculo que pueden emplearse para ejecutar de manera concurrente rutinas comunes
en el proceso de generación del cubo de datos como ordenamientos, operaciones aritméticas y
accesos a datos de gran volumen.
24
1.4.
Alcances y limitaciones
En este trabajo se presenta el diseño e implementación de tres métodos paralelos para la
generación de cubos de datos completos y de tipo iceberg. Los métodos están basados en un
conjunto de primitivas paralelas que aprovechan las caracterı́sticas de una GPU de modelo
reciente y el paralelismo de los CPUs multinúcleo. En el estudio experimental se probaron
medidas distributivas (SUM, MAX, MIN, COUNT) y algebráicas (AVG) utilizando conjuntos
de datos numéricos, evaluando el desempeño del software con respecto a otros métodos bien
conocidos en la literatura de bases de datos como BUC [3] y MM-Cubing [43].
Las principales limitaciones tecnológicas con las que se lidió durante el desarrollo de este
trabajo son:
La tecnologı́a de almacenamiento: A diferencia de los procesadores que incrementan su
desempeño anualmente en un 50 − 60 % aproximadamente, los discos duros y otros dispositivos de almacenamiento secundario logran un incremento solo de 8 − 10 % debido
a limitaciones mecánicas y otros factores, además, las capacidades de almacenamiento se
han incrementado a un ritmo mayor al de la velocidad de transferencia [21]. Esta diferencia
hace cada vez más difı́cil utilizar la capacidad de los discos de manera efectiva, ya que los
tiempos de acceso a memoria secundaria demoran el procesamiento de grandes volúmenes
de datos.
La capacidad del bus PCI Express: Este bus que conecta a la memoria principal de la
computadora con la GPU forma un cuello de botella al realizar un alto número de transferencias. Esta situación se da a causa del tamaño limitado de la RAM de video, ya que
los modelos recientes cuentan entre 1-6 GB.
25
1.5.
Aportaciones
Las aportaciones de esta tesis son:
Un método paralelo de generación de cubos de datos llamado MCBUC que es una versión
replicada del algoritmo BUC [3] que funciona mediante hilos de CPU.
Un nuevo método paralelo de generación de cubos de datos llamado SPCube basado en
primitivas paralelas de GPUs y CPUs multinúcleo que genera vistas del cubo a partir de
otras más detalladas a fin de reducir cálculos.
Un nuevo método paralelo de generación de cubos de datos llamado GPUgenCube que
utiliza primitivas paralelas de GPUs y CPUs multinúcleo. Este método genera grupos de
vistas en paralelo, realizando la agrupación de las vistas en base a sus atributos, ahorrando
también operaciones de ordenamiento.
1.6.
Organización de la tesis
El resto de la tesis está organizado de la siguiente manera:
En el capı́tulo 2 se presentan los fundamentos teóricos sobre el procesamiento analı́tico
en lı́nea y los cubos de datos. Se proporcionan también varios conceptos tecnológicos
referentes al hardware de procesamiento que se utilizó durante el desarrollo de este trabajo
y se presenta una revisión del trabajo previo a esta investigación.
El capı́tulo 3 plantea el modelo de solución, iniciando por describir la configuración del
sistema de cómputo que se utilizó para implementar el software diseñado en esta tesis.
Posteriormente se describe el diseño e implementación de varias primitivas paralelas. Para
finalizar el capı́tulo, se proponen tres métodos paralelos para generación de cubos de datos
completos y de tipo iceberg.
En el capı́tulo 4 se presenta un estudio experimental. Se describe el escenario de pruebas
ası́ como los resultados obtenidos.
Finalmente, en el capı́tulo 5 se dan las conclusiones y directivas para posibles trabajos
futuros.
26
Capı́tulo 2
Fundamentos y trabajos previos
En este capı́tulo se describen los fundamentos que sustentan a este trabajo y los conceptos
necesarios para su lectura auto contenida.
La primera parte del capı́tulo presenta algunos conceptos básicos referentes al OLAP y los
cubos de de datos. Ası́ también se dan las definiciones de las funciones de agregación más comunes (SUM, MAX, MIN, COUNT, AVG) y se recopilan algunas estrategias conocidas para
el cálculo de cubos completos y de tipo iceberg. La segunda parte da un breve repaso sobre el
paralelismo en bases de datos y sus aspectos funcionales. La tercera parte de este capı́tulo presenta algunos detalles de la tecnologı́a de procesamiento multinúcleo y el cómputo de propósito
general en unidades de procesamiento gráfico. Para concluir el capı́tulo dos, se presenta el estado
del arte de esta investigación.
27
2.1.
El procesamiento analı́tico en lı́nea y los cubos de
datos
El procesamiento analı́tico en lı́nea o simplemente OLAP (acrónimo en inglés de On-Line
Analytical Processing) es una solución utilizada en el campo de la Inteligencia de Negocios (en
inglés, Business Intelligence), la cual consiste en consultas a estructuras multidimensionales (o
cubos de datos OLAP) que contienen datos resumidos de grandes bases de datos o sistemas
transaccionales. Se usa en informes de negocios de ventas, marketing, informes de dirección,
minerı́a de datos y áreas similares.
Un sistema OLAP permite a un analista visualizar resúmenes de datos multidimensionales.
La palabra en lı́nea (en inglés, online) indica que el analista debe ser capaz de solicitar nuevos
resúmenes y obtener respuestas en lı́nea, es decir, dentro de unos cuantos segundos, y no debe
estar forzado a esperar un largo tiempo para ver el resultado de una consulta. Las versiones
iniciales de muchas herramientas OLAP asumı́an que los datos estaban residentes en memoria.
El análisis de moderadas cantidades de datos puede realizarse incluso utilizando aplicaciones de
hoja de cálculo, como Excel. Sin embargo, el OLAP sobre grandes cantidades de datos requiere
de la utilización de bases de datos con soporte para pre - procesamiento eficiente de datos
ası́ como de procesamiento en lı́nea de consultas.
Considere una aplicación en que una tienda de dispositivos de electrónicos digitales desea
averiguar cuales son los artı́culos más vendidos. Suponga que los artı́culos están caracterizadas
por su nombre, fabricante y su capacidad, además, que se tiene la relación ventas con el esquema
ventas(Artı́culo, Fabricante, Capacidad, Cantidad). Suponga que el atributo Artı́culo puede
adoptar los valores {Unidad Mini-SATA, Memoria flash, Memoria RAM, Unidad de estado
solido}, Fabricante puede adoptar los valores {F1, F2, F3} y capacidad tomar entre {16GB,
32GB, 128GB}.
Dada una relación utilizada para el análisis de datos se pueden identificar algunos de sus
atributos como atributos de medida, es decir atributos que miden algún valor y pueden agregarse.
Por ejemplo, el atributo Cantidad de la relación ventas es un atributo de medida, ya que en
este caso mide la cantidad de unidades vendidas. Algunos otros atributos de la relación (pueden
ser todos) se identifican como atributos de dimensión, ya que definen las dimensiones en las
que se ven los atributos de medida y los resúmenes de los atributos de medida. En la relación
ventas, Artı́culo, Fabricante y Capacidad son atributos de dimensión. Los datos que pueden
modelarse como atributos de dimensión y como atributos de medida se denominan atributos
dimensionales.
28
Para analizar los datos multidimensionales puede que el analista desee ver los datos dispuestos como se encuentran en la tabla de la Figura 2.1. La cual muestra las cifras totales de
diferentes combinaciones de Artı́culo y Fabricante. El valor para atributo capacidad en este caso
es todas, esto es, los valores mostrados son resumen para todos los valores de la tabla. Para
representar la situación de este atributo se utiliza el valor especial ALL.
Figura 2.1: Tabla cruzada de la relación ventas sobre los atributos Artı́culo y Fabricante. En este caso, el atributo
capacidad tiene asignado el valor especial ALL
La tabla mostrada en la Figura 2.1 es un ejemplo de tabla cruzada o tabla dinámica. En
general las tablas cruzadas son tablas en donde los valores de uno de los atributos forman las
cabeceras de las filas, los valores del otro atributo forman las cabeceras de las columnas y los
valores de cada celda se obtienen de la siguiente manera: considere 2 atributos A y B, cada
celda puede identificarse como (ai , bj ) donde ai es un valor de A y bi es un valor de B. El valor
de la celda (ai , bj ), se obtiene mediante la agregación de las tuplas correspondientes (si es que
existen en la relación). En este ejemplo la agregación utilizada es la suma sobre los valores del
atributo número para todos los valores de capacidad, como se indica por capacidad ALL en
la tabla de la Figura 2.1. En este caso la tabla cruzada también tiene una columna y una fila
adicionales que guardan los totales de las celdas de cada fila o columna. La mayor parte de la
tablas cruzadas tienen esas filas y columnas de resumen.
Las tablas cruzadas son diferentes de las tablas comunes que normalmente se guardan en
las bases de datos relacionales, ya que el número de columnas de la tabla cruzada depende de
los datos. Una modificación en los valores de los datos puede dar lugar a que se añadan más
columnas, lo que no resulta deseable para el almacenamiento de los datos. No obstante, la vista
de tabla cruzada es deseable para mostrarla al usuario.
29
La representación de las tablas cruzadas sin valores de resumen en formato tabular con
un número fijo de columnas es sencilla. La tabla cruzada con columnas o filas resumen puede
representarse introduciendo el valor especial ALL para representar los subtotales, como en la
Tabla 2.1. La norma SQL:1999 utiliza realmente el valor null, para evitar confusiones, se
utilizará el valor ALL.
Artı́culo
Fabricante
Capacidad Cantidad
Unidad Mini-SATA
Unidad Mini-SATA
Unidad Mini-SATA
Unidad Mini-SATA
Memoria flash
Memoria flash
Memoria flash
Memoria flash
Memoria RAM
Memoria RAM
Memoria RAM
Memoria RAM
Unidad de estado solido
Unidad de estado solido
Unidad de estado solido
Unidad de estado solido
ALL
ALL
ALL
ALL
F1
F2
F3
ALL
F1
F2
F3
ALL
F1
F2
F3
ALL
F1
F2
F3
ALL
F1
F2
F3
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
ALL
8
35
10
53
20
10
5
35
14
7
28
49
20
2
5
27
62
54
48
164
Tabla 2.1: Representación tabular de los datos en la Figura 2.1.
Ahora bien, considere las tuplas (Unidad Mini-SATA, ALL, ALL) y (Memoria flash, ALL,
ALL). Las cuales se han obtenido eliminando las tuplas individuales con diferentes valores de
fabricante y capacidad, ası́ como sustituyendo el valor de número por un agregado, en este
caso una suma. El valor ALL puede considerarse como una representación del conjunto de los
valores de un atributo. Las tuplas con el valor ALL para las dimensiones fabricante y capacidad
pueden obtenerse mediante una agregación de la relación ventas agrupando con respecto al
atributo Artı́culo. De manera similar se puede utilizar una agrupación con respecto a fabricante
y capacidad para obtener las tuplas con el valor ALL en Artı́culo y una agrupación sin atributo
30
alguno para obtener la tupla con el valor ALL en los atributos Artı́culo, Fabricante y Capacidad.
La generalización de las tablas cruzadas de dos dimensiones a n dimensiones puede visualizarse como una estructura de n dimensiones, denominada cubo de datos, en este caso, a cada
tabla cruzada se le llama vista o cuboide del cubo de datos. La Figura 2.2 muestra un cubo de
datos para la relación ventas. El cubo de datos tiene tres dimensiones, Artı́culo, Fabricante y
Capacidad, el atributo de medida es número. Cada celda se identifica por los valores de estas
tres dimensiones. Cada celda del cubo de datos contiene un valor, igual que en la tabla cruzada.
En la Figura 2.2, el valor contenido en la celda se muestra en una de las caras de la celda; las
otras caras de la celda se muestran en blanco si son visibles. Todas las celdas contienen valores
aunque no sean visibles.
Si el valor de una dimensión es ALL, entonces la celda afectada contendrá un resumen
de todos los valores de esa dimensión, como en las tablas cruzadas. El número de maneras
diferentes en que las tuplas pueden agruparse para su agregación es 2n , esto es, para una tabla
con n dimensiones, se puede realizar la agregación de sus tuplas con respecto a la agrupación
de cada uno de los 2n subconjuntos de las n dimensiones.
Figura 2.2: Conceptualización de un cubo de datos de tres dimensiones. El cubo de datos muestra los totales de
ventas para una tienda de dispositivos electrónicos dispuestos de acuerdo a toda combinación de Artı́culo, Fabricante
y Capacidad. Por ejemplo, se puede ver que se vendieron 20 unidades de estado solido del fabricante F1, 7 de 128GB,
12 de 32GB y 1 de 16GB.
31
2.1.1.
Funciones de agregación
Las funciones de agregación toman una colección (un conjunto o multiconjunto) de valores
como entrada y devuelven un solo valor. Recordando la explicación anterior sobre las tablas
cruzadas y su generalización como un cubo de datos, los valores para construir estas tablas o
vistas de cubo se obtienen mediante funciones de agregación, es decir, estas funciones se aplican
a conjuntos de tuplas agrupados de cierta manera para dar origen a los valores agregados de las
vistas del cubo. El SQL ofrece cinco funciones básicas:
MIN: Regresa el valor más pequeño dentro de un conjunto de datos.
MAX: Regresa el valor más grande dentro de un conjunto de datos.
SUM: Regresa el total de un conjunto de valores numéricos.
COUNT: Regresa el número de elementos en un conjunto.
AVG: Regresa el centro de un conjunto de datos (media aritmética). Sea x1 , x2 , x3 , ..., xN
un conjunto de N valores, tales como
los de algún atributo numérico como salario, la
PN
x
i
media del conjunto está dada: x̄ = i=1
= x1 ,x2 ,xN3 ,...,xN
N
Las funciones de agregación pueden clasificarse en tres categorı́as [16]. Considere la agregación
de un conjunto de tupas T . Sea {Si |i = 1, ..., n} cualquier conjunto completo de subconjuntos
disjuntos T tal que ∪i Si = T y ∩i Si = ∅.
Una función de agregación F es distributiva si existe una función G tal que F (T ) =
G({F (Si |i = 1, ..., n}). SUM, MIN y MAX son distributivas con G = F . COUNT es
distributiva con G = SUM.
Una función de agregación F es algebraica si existe una función G cuyo valor de retorno
es una M -tupla y una función H tal que F (T ) = H({G(Si )|i = 1, ..., n}) y M es constante
independiente de |T | y n. Todas las funciones distributivas son algebraicas, ası́ como lo son
la media aritmética (AVG) y la desviación estándar (σ). Para AVG, la función G produce
SUM y COUNT, y H obtiene SUM/COUNT.
Una función de agregación F es holı́stica si no es algebraica. Por ejemplo, la mediana y el
rango (RANK) son holı́sticas.
En esta investigación las funciones evaluadas son distributivas y algebraicas.
32
2.1.2.
Operadores GROUP BY y CUBE BY
Dado que las funciones de agregación regresan un solo valor, utilizando el operador GROUP
BY el SQL es posible crear una tabla de varios valores agregados indexados por un grupo de
atributos. Por ejemplo, la siguiente consulta reporta el total de ventas para cada cada producto
en cada tienda:
SELECT
id producto, id tienda, SUM(ventas unitarias)
FROM
tienda
GROUP BY id producto, id tienda
El operador GROUP BY particiona la relación en conjuntos disjuntos de tuplas y entonces
agrega sobre cada conjunto de tuplas. Véase la Figura 2.3.
Figura 2.3: Operador relacional GROUP BY: Particiona una tabla en grupos. Cada grupo es agregado por una función.
La función de agregación sumariza alguna columna de grupos regresando un valor por cada grupo.
Como una extensión al SQL, el operador CUBE BY fue introducido por Jim Gray y otros
en [16]. Su función es generalizar el operador GROUP BY para calcular agregados para toda
combinación del conjunto de atributos que se ha especificado. Por ejemplo, consideremos una
relación ventas(Modelo, Año, Color, Unidades), aplicando al operador CUBE BY sobre esta
relación, indicando los atributos Modelo, Año, Color y el agregando SUM sobre el atributo
Unidades, el resultado contendrá la sumatoria de ventas para toda la relación, para cada atributo (Modelo), (Año), (Color), para cada par (Modelo, Año), (Modelo, Color), (Año, Color) y
finalmente para (Modelo, Año, Color).
La Figura 2.4 muestra el resultado en formato tabular de la ejecución del operador CUBE
BY para la construcción de un cubo de datos a partir de la relación ventas antes mencionada.
Como puede observarse, hay tuplas para toda combinación de Modelo, Año y Color. El operador
CUBE BY no es estándar, por lo que no todos los manejadores de bases de datos modernos lo
incluyen.
33
Figura 2.4: Cubo de datos sobre una relación de ventas de automóviles.
2.1.3.
Generación del cubo de datos
La generalización o agregación de los datos es un proceso que abstrae un conjunto grande
de datos de datos relevantes a una tarea en una base de datos partiendo de un nivel conceptual
relativamente bajo pasando a niveles conceptualmente más altos. Los usuarios de sistemas OLAP
requieren obtener con facilidad y sencillez grandes conjuntos de datos resumidos en forma clara
y concisa, a diferentes niveles de detalle y desde diferentes ángulos. Tales descripciones ayudan
a proporcionar una visión general de un conjunto de datos.
Para una relación R con n atributos o dimensiones más un atributo de medida M , R(A1 , A2 ,
..., An , M ), el problema básico del cálculo del cubo de datos implica la agregación de R para
construir 2n grupos de tuplas respecto a toda posible combinación de las n dimensiones (i.e., el
conjunto potencia de las n dimensiones de R), a cada uno de estos grupos de tuplas se le llama
cuboide o vista del cubo. Cada cuboide corresponde a un conjunto de tuplas donde uno o más
de los elementos de cada tupla es un valor agregado que se calculó a partir de una partición de
la relación en base a la cual se generó el cubo de datos [16].
34
Normalmente el tamaño del cubo de datos supera por mucho al de la relación a partir
de la cual fue calculado. El tamaño de cada agrupamiento o cuboide está en función de las
cardinalidades de sus dimensiones, posiblemente, esta cantidad es equivalente al producto de
Pn
tales cardinalidades, i.e., Tamaño del cubo ≈
i=1 |A1i | × |A2i | × ... × |Ami | donde m es el
número de atributos de la vista. Cuando el producto de las cardinalidades para un agrupamiento
es grande con respecto al número de celdas que realmente aparecen en un cuboide, se dice que
el cuboide es disperso (en inglés, sparse). Cuando el número de cuboides dispersos es grande
con respecto al número total de cuboides, se dice que el cubo es disperso.
El cubo iceberg
Como una especialización al problema del cálculo del cubo de datos, el cubo iceberg fue
introducido en [3] y consiste en calcular la parte de los cuboides que cumple con un umbral
de soporte mı́nimo previamente especificado por el usuario. Si existe una condición iceberg, la
cual toda partición de la relación base debe cumplir, por ejemplo SUM(ventas)≥10, la tarea es
calcular un cubo iceberg (véase la Figura 2.5), de lo contrario se calcula el cubo completo.
Figura 2.5: Conceptualización de un cubo iceberg de tres dimensiones con umbral de soporte mı́nimo SUM(ventas)≥10.
El cubo iceberg muestra los totales de ventas para una tienda de dispositivos electrónicos dispuestos de acuerdo a
toda combinación de Artı́culo, Fabricante y Capacidad. Se puede ver que las vistas solo incluyen valores agregados
que satisfacen el umbral de soporte mı́nimo.
La relación base y sus valores únicos de atributos pueden dar origen a un cubo de datos
enorme, metafóricamente esto puede visualizarse como un iceberg completo, mientras tanto, la
respuesta es pequeña, es decir, el número de tuplas que satisfacen el umbral es menor, esto
35
representarı́a la punta del iceberg, la parte de los cuboides que es de más interés.
En otras palabras, la generación del cubo iceberg permite calcular selectivamente las tuplas
que satisfacen una condición de agregación. De manera similar a lo que sucede en una consulta
SQL que utiliza la cláusula HAVING, cuando se genera una vista o cuboide del cubo iceberg
se remueven ciertas tuplas cuyos valores agregados caen bajo el umbral de soporte mı́nimo.
Por ejemplo, para una relación R(A1 , A2 , ..., An , M1 , M2 , ..., Mm ) y un umbral N donde los Ai
son atributos de agrupamiento y las Mi agregaciones sobre algún Ai , la consulta serı́a como se
muestra a continuación:
SELECT
A1 , A2 , ..., An , M1 , M2 , ..., Mm
FROM
R
GROUP BY A1 , A2 , ..., An
HAVING
Mi ≥ N
Donde Mi ≥ N es la condición de soporte mı́nimo que toda tupla resultante debe cumplir.
Algoritmos secuenciales para generación de cubos de datos
La mayorı́a de los algoritmos para cálculo de cubos de datos utilizan a la estructura de
lattice para conceptualizar la jerarquı́a entre las vistas del cubo. La Figura 2.6 muestra una
lattice para un cubo de cuatro dimensiones (A, B, C y D). Los nodos en la lattice representan
vistas o cuboides del cubo de datos, mismos que se encuentran etiquetados de acuerdo a sus
atributos de agrupamiento. Los arcos de la estructura de lattice muestran rutas potenciales de
calculo. En su mayorı́a, los algoritmos para calculo de cubos de datos convierten esta estructura
en un árbol de procesamiento dirigido. Por tanto, cada nodo del árbol de procesamiento tiene
solo un padre, ya que se calcula a partir de su padre o de los datos en bruto.
Figura 2.6: Estructura de lattice para un cubo de datos de cuatro dimensiones.
36
Los algoritmos de cubos de datos normalmente realizan el cálculo de los cuboides de alguna
manera en particular. Los algoritmos que siguen los arcos de la lattice desde los datos en bruto
hacia el valor del agregado total (ALL) son conocidos como algoritmos descendentes (en inglés,
top-down). Los algoritmos que calculan los cuboides en reversa se les llama ascendentes (en
inglés, bottom-up). Actualmente se conocen algoritmos como [49] y [43] que no siguen ninguno
de estos dos modelos. La Figura 2.7 muestra un ejemplo del enfoque descendente, de ahı́ puede
observarse que los cuboides se calculan partiendo de ABCD hasta A.
Figura 2.7: Ejemplo de árbol de procesamiento presente en algoritmos descendentes.
Por otro lado, el enfoque ascendente va en la dirección opuesta. La Figura 2.8 ilustra un
árbol de procesamiento de un algoritmo de tipo ascendente, los números indican el orden en
que se calculan los cuboides.
Figura 2.8: Ejemplo de árbol de procesamiento presente en algoritmos ascendentes.
37
Estrategias en algoritmos descendentes
Generalmente los algoritmos de cubos de datos intentan descubrir y aprovechar los elementos comunes entre un nodo y su padre en la estructura de lattice. Varios elementos han sido
explotados por los algoritmos descendentes, algunas de estas técnicas fueron listadas en [1]:
Padre de menor costo (en inglés, Smallest - parent): Tiene el objetivo de calcular un
couboide a partir del más pequeño previamente calculado. Por ejemplo, considerando la
Figura 2.7, es posible calcular AB a partir de ABC y ABD, sin embargo, de entre los dos
potenciales padres, se selecciona el de menor tamaño, ya que calcular a partir del padre
de menor tamaño dará lugar a un menor costo.
Cacheo de resultados (en inglés, Cache - results): Esta técnica trata de calcular un cuboide cuando su padre está todavı́a en memoria, reduciendo costos en operaciones de
entrada/salida a disco.
Amortizar escaneos (en inglés, Amortize - scans): Esta técnica también tiene por objetivo
reducir las operaciones de entrada/salida a disco, esto se realiza amortizando las lecturas
a través del calculo de tantos couboides como sea posible juntos en memoria. Por ejemplo,
considerando la Figura 2.7, durante el escaneo del cuboide ABCD, es posible calcular
ABC, ACD, ABD, BCD al mismo tiempo.
Compartir ordenamiento (en inglés, Share - sorts): Algunos usan esta técnica para compartir costos de ordenamiento entre múltiples cuboides.
Compartir particionamiento (en inglés, Share - partitions): Esta técnica es propia de los
algoritmos basados en tablas hash. Cuando una tabla hash no cabe en memoria, los datos
son particionados en chunks que si caben en memoria. Habiendo leı́do un chunk, se calculan
múltiples cuboides con el fin de compartir costos de particionamiento.
Estrategias en algoritmos ascendentes
El primer método para generar el cubo de datos de tipo ascendente fue introducido en [3]
por K. Beyer y R. Ramakrishnan, el cual se especializa en el cálculo de cubos iceberg reduciendo
el cálculo a través de una estrategia de poda Apriori [2]. La asignación de umbrales de soporte
mı́nimo en las consultas iceberg permite remover gran cantidad de tuplas en los cuboides.
38
2.2.
Paralelismo en bases de datos
La arquitectura de un sistema de bases de datos esta influida en gran medida por el sistema
informático subyacente en el que se ejecuta, en concreto, por aspectos de la arquitectura de
la computadora como la conexión a la red, el paralelismo y la distribución. El procesamiento
paralelo dentro de una computadora permite acelerar las actividades del sistema de base de
datos, proporcionando respuestas más rápidas a las transacciones ası́ como la capacidad de
ejecutarlas en mayor número. Las consultas pueden procesarse de manera que se explote el
paralelismo ofrecido por el sistema subyacente. Los sistemas paralelos mejoran el desempeño
en el procesamiento y la velocidad en operaciones de entrada y salida de datos ya que CPUs,
memorias y discos funcionan en paralelo. Este tipo de sistemas va siendo cada vez más común,
lo que hace muy importante su estudio y aplicación en las bases de datos.
Una de las principales razones que ha impulsado el desarrollo de sistemas de bases de datos
paralelos es el manejo de datos extremadamente grandes (del orden de terabytes) o que deben procesar gran número de transacciones por segundo (del orden de miles de transacciones
por segundo), es aquı́ donde las bases de datos centralizadas y cliente-servidor son incapaces
de soportar tales aplicaciones. En el procesamiento paralelo se realizan una gran cantidad de
operaciones simultáneamente, mientras que en el procesamiento secuencial se realizan en serie.
El procesamiento en paralelo puede ser clasificado como de grano grueso (en inglés, coarse
grained system) o de grano fino (en inglés, fine grained system), donde el grano grueso se refiere
a los sistemas con un pequeño número de procesadores potentes, comúnmente de dos a 16
procesadores, mientras que el grano fino define a una máquina masivamente paralela, que cuenta
con cientos de procesadores más pequeños y es capaz de soportar un grado de paralelismo mucho
mayor. Hoy en dı́a, las recientes GPUs son claro ejemplo de máquinas masivamente paralelas,
por tanto, su aplicación a las bases de datos es un área que ha comenzado a ser objeto de
estudio, como es el caso de este trabajo.
39
2.2.1.
Paralelismo a gran escala
En la actualidad existen sistemas de cómputo en paralelo a gran escala como los clusters de
computadoras que a diferencia de las GPUs operan mediante procesadores débilmente acoplados.
Estos sistemas consisten en un grupo (decenas o cientos) de computadoras conectadas a través
de una red para trabajar en conjunto, de manera que pueden verse como un solo sistema. Para
este tipo de procesadores existen modelos de programación como MPI [12] y MapReduce [10].
MPI es un sistema estandarizado y portable de paso de mensajes diseñado por un grupo de
investigadores de la academia y la industria para funcionar sobre una variedad de computadoras paraleles. El estándar define la sintaxis y semántica de una biblioteca de rutinas aplicable
a un amplio rango de usuarios que desean escribir programas de paso de mensajes con portabilidad empleando lenguajes de programación como C o Fortran. Actualmente existen diversas
implementaciones de MPI, algunas disponibles en la web para uso académico y comercial como
OpenMpi [29].
Por otra parte, MapReduce es un modelo de programación y una implementación asociada
para procesar y generar grandes conjuntos de datos. Los usuarios especifican una función MAP
que procesa un par clave/valor para generar un conjunto intermedio de pares clave/valor y una
función REDUCE que une todos los valores intermedios asociados con la misma clave intermedia.
Empleando MapReduce, los programas escritos en este estilo funcional son automáticamente
paralelizados y ejecutados en un cluster de computadoras comunes. El sistema de tiempo de
ejecución de MapReduce se encarga de detalles como particionar los datos de entrada, planificar
la ejecución del programa a través de un conjunto de máquinas, manejar fallos de máquinas y
administrar la comunicación entre máquinas que sea necesaria. Esto permite a los programadores
sin experiencia en sistemas paralelos y distribuidos aprovechar fácilmente los recursos de un gran
sistema distribuido.
40
Como ejemplo de este modelo de programación, considere el problema de contar el número
de ocurrencias de cada palabra en una gran colección de documentos. El usuario escribirı́a para
ello un código similar al siguiente pseudocódigo:
Algoritmo 1 conteoPalabras
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Función map(clave, valor)
. clave: nombre de un documento, valor: contenidos
Para cada palabra w en valor hacer
EmitirIntermedio(w,“1”)
Fin Para
Fin Función
Función reduce(clave, valores)
. clave: una palabra, valores: una lista de conteos
resultado ← 0
Para cada v en valores hacer
resultado ← resultado + v
Emitir(resultado)
Fin Para
Fin Función
La función MAP emite cada palabra más un conteo asociado de ocurrencias (solo “1” en
este sencillo ejemplo). La función REDUCE suma todos los conteos emitidos para una palabra
en particular.
Adicionalmente, el usuario escribe código para completar un objeto de especificación MapReduce con los nombres de los archivos de entrada y salida, ası́ como algunos parámetros opcionales
de afinación. El usuario invoca la función MapReduce, pasando el objeto de especificación.
Una de las implementaciones más populares de MapReduce es Apache Hadoop [13]. Este
software es una biblioteca de funciones que permite distribuir el procesamiento de grandes conjuntos de datos a través de clusters de computadoras usando sencillos modelos de programación.
Además, este proyecto es de código abierto, lo que permite su utilización sin cargos por licencias.
41
2.2.2.
Arquitecturas paralelas
En los sistemas de bases de datos paralelos se pueden encontrar aspectos funcionales que los
hacen diferentes. Más especı́ficamente, existen diversas arquitecturas para máquinas paralelas,
de manera breve los modelos básicos son:
Memoria compartida: Todos los procesadores comparten una memoria común.
Disco compartido: Todos los procesadores comparten un conjunto de discos.
Sin compartimiento: Los procesadores no comparten memoria ni discos.
Hı́brida: Trata de combinar los beneficios de las anteriores
En este trabajo se empleo la combinación CPU - GPU, que opera esencialmente como una
máquina de arquitectura de memoria compartida, por lo que se describirá en lo que resta de la
sección.
Memoria compartida
Debido que los procesadores están interconectados entorno a una memoria principal y uno
o varios discos compartidos, los GPUs y CPUs multinúcleo funcionan de manera similar a la
arquitectura de memoria compartida.
En el enfoque de memoria compartida, cualquier procesador tiene acceso a cualquier módulo
de memoria o unidad de disco a través de una conexión rápida (por ejemplo, un bus de alta
velocidad), véase la Figura 2.9. Todos los procesadores están bajo el control de un solo sistema
operativo. El beneficio de esta arquitectura es la eficiencia en la comunicación entre procesadores,
esto es, cualquier procesador puede acceder a los datos en la memoria compartida sin necesidad
de la intervención de software. Los procesadores pueden enviarse mensajes a través de la escritura
en memoria, por tanto la comunicación es muy rápida. Por ende, en este enfoque es posible
que los procesadores compartan información de control (bloqueos a tablas) y meta-información
(directorio) de una base de datos, ası́, el desarrollo de software de base de datos para esta
arquitectura no difiere demasiado del realizado en sistemas con un solo procesador.
42
En este tipo de arquitectura, como es el caso de las GPUs y CPUs multinúcleo, los procesadores suelen contar con una memoria caché de tamaño considerable para reducir los accesos a
memoria compartida, sin embargo, esta reducción no puede lograrse siempre, por que debido a
su menor tamaño en comparación con la memoria compartida, no todos los datos podrı́an caber
en ella. Además, se necesita mantener la coherencia en las cachés, por ejemplo, si un procesador
realiza una escritura en cierta posición de la memoria compartida, los datos de dicha posición
deben actualizarse en todos los procesadores que los mantienen en caché, este mantenimiento
de coherencia sobrecarga el sistema conforme aumenta el número de procesadores.
Figura 2.9: Representación conceptual de la arquitectura memoria compartida. Cualquier procesador tiene acceso a
cualquier módulo de memoria o unidad de disco a través de una conexión rápida.
43
2.3.
Tecnologı́a multinúcleo y de muchos núcleos
Desde el 2003, la industria de semiconductores se ha basado en dos principales enfoques
para el diseño de procesadores [48], los enfoques multinúcleo y de muchos núcleos. El enfoque multinúcleo persigue el sustento de la velocidad en la ejecución de programas secuenciales
utilizando varios procesadores integrados dentro de una sola unidad de cómputo. Es común encontrar chips de entre dos y ocho núcleos, donde cada núcleo es un procesador que implementa
el conjunto completo de instrucciones x86. En contraste, el enfoque de muchos núcleos busca el
mejoramiento en el desempeño de las aplicaciones paralelas. Los ejemplos más notables de este
enfoque son las GPUs, cuyo número de núcleos se duplica en cada nueva generación.
Las GPUs han mostrado un mejor desempeño que los CPUs en tareas concurrentes, esto
se debe a las diferencias de diseño en estos dos tipos de procesadores, véase la Figura 2.10. A
diferencia de las GPU, los diseños de CPU están optimizados para mejorar el desempeño de
código secuencial, mediante una sofisticada lógica de control.
Figura 2.10: Representación de los diseños de procesadores multinúcleo (CPU) y de muchos núcleos (GPU) respectivamente. Los procesadores multinúcleo normalmente tienen entre dos y ocho núcleos, orientados a la aceleración de
procesos secuenciales. En contraste, las GPUs están enfocadas a problemas paralelos, contando con cientos de núcleos
menos potentes que los de una CPU multinúcleo.
Ası́, las GPUs están diseñadas como motores de calculo numérico y por lo tanto no se
desempeñarı́an adecuadamente en algunas tareas para las que los CPUs están diseñados. En
esta tesis se diseñaron métodos que utilizan estas dos tecnologı́as dentro de distintas fases del
proceso de construcción del cubo de datos.
44
2.3.1.
Procesamiento paralelo en CPUs multinúcleo
En arquitecturas de multiprocesadores de memoria compartida, como los multiprocesadores
simétricos (en inglés, Symmetric multiprocesors), los hilos pueden usarse para implementar
paralelismo. Para los sistemas UNIX y derivados, se ha especificado una interfaz estándar para
manejo de hilos usando el lenguaje C a través del estándar IEEE POSIX 1003.1c [23]. Todo el
software derivado de este trabajo se desarrolló en un sistema Linux y se utilizaron este tipo de
hilos, comúnmente conocidos como hilos POSIX.
Un hilo está definido como un flujo independiente de instrucciones que puede ser planificado
por el sistema operativo para ejecutarse. Es decir, para el programador, un hilo podrı́a ser
descrito por el concepto de un procedimiento que se ejecuta de manera independiente respecto
a su programa principal o proceso padre. En UNIX, los procesos son creados por el sistema
operativo y requieren de una buena cantidad de información acerca de recursos de programa y
estado de ejecución de programa, entre los que podemos mencionar:
ID de proceso, ID de grupo de proceso, ID de usuario e ID de grupo
Entorno, directorio de trabajo
Instrucciones de programa, registros, pila (en inglés, stack )
Descriptores de archivos, bibliotecas compartidas
Herramientas para comunicación entre procesos (e.g., colas de mensajes, tuberı́as, semáforos o memoria compartida).
La Figura 2.12 muestra una representación de un proceso de UNIX.
Figura 2.11: Representación conceptual de un proceso de UNIX. Un proceso de UNIX cuenta con recursos que permiten
la ejecución de un programa como una pila, texto de programa y datos usados por el programa.
45
Los hilos usan y existen dentro de esos recursos de los procesos, sin embargo son capaces
de ser planificados por el sistema operativo y ejecutarse como entidades independientes en gran
parte porque duplican solo los recursos esenciales que los habilitan para existir como código
ejecutable. La Figura 2.12 muestra una representación de dos hilos coexistiendo dentro de un
proceso de UNIX.
Figura 2.12: Representación conceptual de proceso de UNIX con dos hilos. Los hilos duplican los recursos que les
permiten existir como código ejecutable dentro de un proceso de UNIX.
De manera que el flujo de control independiente de los hilos se logra a gracias a que cada
una de estas entidades cuenta con elementos individuales como:
Apuntador a pila
Registros
Propiedades de planificación
Conjunto de señales de pendiente y bloqueado
Datos especı́ficos del hilo
46
2.3.2.
Cómputo de propósito general en unidades procesamiento gráfico
Conforme los diseños de hardware de las GPUs fueron evolucionando, incluyendo cada vez
más procesadores, su arquitectura se fue asemejando a las de las computadoras paralelas de alto
desempeño. Con la llegada de las GPUs con soporte DirectX 9, algunos investigadores notaron la
tendencia de incremento en el desempeño de las GPUs y comenzaron a explorar esta tecnologı́a
a fin de poder aplicarla en la resolución de problemas de ciencia e ingenierı́a, sin embargo, en
ese momento las GPUs estaban diseñadas únicamente para satisfacer las necesidades de las
interfaces gráficas, por lo tanto, para acceder estos recursos de cómputo, los programadores
tenı́an que traducir su trabajo en operaciones gráficas nativas y ası́ el calculo podı́a ser resuelto
mediante llamadas a interfaces gráficas como OpenGL [36] o DirectX [28]. Esta técnica es
llamada cómputo de propósito general en unidades de procesamiento gráfico, y fue ampliamente
utilizada hasta el 2006, resultando complicada la utilización de las GPUs en aplicaciones de
propósito general hasta ese momento, ya que los programadores debı́an tener conocimiento
sobre herramientas como OpenGL y DirectX.
NVIDIA CUDA
CUDA [33] es el acrónimo de Compute Unified Device Architecture y es una arquitectura
de cómputo en paralelo de propósito general, introducida por la corporación NVIDIA [35] en
noviembre de 2006. CUDA tiene el propósito de aprovechar a las GPUs NVIDIA como motores
de cómputo en paralelo para resolver problemas complejos de cálculo de una manera más eficiente que en un CPU. Esta arquitectura de hardware y software permite que las GPUs NVIDIA
ejecuten programas escritos en lenguajes de programación como C, C++, Fortran, OpenCL,
etc.
Además del paralelismo proporcionado por los hilos POSIX, en este trabajo se utilizó esta
plataforma para aprovechar la capacidad de cómputo de los números procesadores de una GPU.
Una GPU es una máquina masivamente paralela conectada a un equipo de cómputo convencional
a través de un bus PCI Express, la Figura 2.13 muestra la organización tı́pica de una GPU
NVIDIA de modelo reciente.
47
Figura 2.13: Organización tı́pica de una GPU conformada por 30 multiprocesadores, cada uno con 8 ALUs SIMD y
una espacio de memoria local compartida.
En el modelo CUDA un programa es ejecutado a través de un sistema anfitrión o host y
un dispositivo o Device, con anfitrión se hace referencia un equipo de cómputo, mientras que el
dispositivo es la GPU. Generalmente el sistema anfitrión se encarga de iniciar el programa y de
ejecutar las partes secuenciales mientras que la GPU realiza las paralelas.
Un programa en CUDA invoca de manera serial o paralela a funciones llamadas kernel. Un
kernel es a groso modo una función que se ejecuta mediante la GPU a través de un bloque de
hilos paralelos. El programador o compilador responsable de organizar esos hilos en bloques y
mallas de bloques de hilos que tienen acceso a diferentes niveles de memoria en la GPU. Es
decir, un bloque de hilos es esencialmente un conjunto de hilos ejecutándose en paralelo, los
cuales pueden cooperar entre ellos mismos con la barrera que representa la sincronización y la
memoria que comparten.
De manera que en la GPU se crean instancias de un kernel en una malla de bloques de hilos,
donde cada hilo dentro del bloque se encarga de ejecutar una instancia del kernel y consta de un
atributo identificador, un contador de programa, registros, memoria privada por hilo, entradas
y salidas de resultados.
Una malla es un arreglo de bloques de hilos que ejecutan el mismo kernel, leen la entrada de
la memoria global, escriben la salida a la memoria global y se sincronizan a través de llamadas
dependientes de kernel. En el modelo de programación CUDA, cada hilo tiene una espacio de
memoria individual usado para volcar el contenido de los registros, llamadas a funciones y variables arreglo automáticas del lenguaje C. Cada bloque de hilos tiene un espacio de memoria
compartido individual usado para la comunicación entre hilos, intercambio de datos y de resultados en algoritmos paralelos. Las mallas de bloques de hilos comparten resultados en espacio
memoria global después de que se realiza la sincronización global de los kernels. La Figura 2.14
conceptualiza esta jerarquı́a.
48
Figura 2.14: Jerarquı́a de hilos, bloques y mallas en CUDA con sus respectivos espacios de memoria.
La jerarquı́a de hilos de CUDA está relacionada con la jerarquı́a de procesadores de la GPU.
Más especı́ficamente, una GPU ejecuta una o más mallas de funciones kernel, un multiprocesador
ejecuta uno o más bloques de hilos, mientras que los núcleos CUDA y otras unidades de ejecución
dentro del multiprocesador ejecutan hilos. Fı́sicamente, los multiprocesadores ejecutan hilos en
grupos de 32, a los cuales se les llama warps. Aunque en la mayorı́a de los casos por sencillez
se puede ignorar la ejecución en warps, el desempeño de los programas mejora de manera
importante si los hilos dentro de un warps ejecutan el mismo código y acceden direcciones
cercanas de memoria.
49
2.4.
Estado del arte
Varios trabajos han aportado métodos secuenciales para el calculo eficiente de cubos de
datos completos y de tipo iceberg, dentro de esta categorı́a se destacan los métodos BUC [3],
Star-Cubing [49] y MM-cubing [43] otros más como [31] han aportado métodos paralelos en
clusters de computadoras. Más recientemente, conforme el uso de las GPUs para la resolución
de problemas de cómputo fue ganando aceptación, en el área de los almacenes de datos y el
OLAP se desarrollaron trabajos como [39], [26], [51] y [47] que de manera similar al presente,
están enfocados al aprovechamiento de la tecnologı́a de GPUs y CPUs multinúcleo.
En el Centro de Investigación en Computación se han realizado también trabajos sobre
cubos de datos como [27] que presenta una técnica para realizar minerı́a de datos o descubrir
conocimiento en datos generalizados y sumarizados mediante cubos. En el artı́culo citado se
presenta una herramienta que permite definir y utilizar cubos de datos, eligiendo las regiones
de interés y definiendo los patrones de comportamiento o situaciones anómalas a localizar en
las regiones de interés. Ası́ mismo, se han realizado tesis sobre paralelización de consultas SQL
como [38] donde se construyó una versión del operador de junta natural (en inglés, natural join)
que funciona a través de una GPU utilizando como base al motor de base de datos conocido
como SQLite [45]. El operador de junta natural permite realizar la combinación de los registros
de dos o más tablas de una base de datos, la junta es un medio para combinar los campos de dos
o más tablas mediante el uso de valores en común. En [7] se propuso un sistema de integración
de bases de datos utilizando un enfoque semiautomático para combinar resultados parciales de
consultas a distintas bases de datos y obtener los resultados relevantes, simplificando al mismo
tiempo la formulación de consultas complejas.
En otras ramas del área de bases de datos, el uso de GPUs y CPUs se ha hecho presente en
trabajos como [25], donde se implementaron consultas por contenido a través de búsquedas de
similitud usando una GPU, es decir consultas donde no se buscan objetos exactamente iguales
al de la consulta, si no objetos similares, lo que implica medir la disimilitud entre el objeto de
consulta y cada objeto de la base de datos. En el artı́culo citado se utiliza el paradigma conocido como espacio métrico (en inglés metric space) que permite modelar problemas de búsqueda
por similitud; se utiliza una estructura de datos llamada permutation index [9] adaptada para
funcionar mediante la arquitectura de una GPU. En [14] se presenta un análisis de desempeño
de la estructura de datos conocida como arreglo de sufijos (en inglés, suffix array) en un multiprocesador con 32 núcleos. Un arreglo de sufijos es una estructura de datos que contiene todos
los sufijos de una cadena dada; normalmente se utilizan para resolver consultas complejas en
aplicaciones relacionadas con bases de datos de texto.
En lo que resta de la sección se describen brevemente varios trabajos relacionados con esta
50
investigación, mismos que se encuentran clasificados de acuerdo a su enfoque de generación del
cubo de datos.
2.4.1.
Generación secuencial de cubos de datos
Calculo ascendente de cubos dispersos y de tipo iceberg (tı́tulo en inglés, BottomUp Computation of Sparse and Iceberg Data Cubes)
El BUC [3] fue el primer algoritmo en abordar el problema del cubo iceberg. Es un algoritmo
de tipo ascendente ya que inicia calculando las vistas menos detalladas del cubo de datos. El
BUC es un método recursivo, inicia siempre por generar la vista menos detallada del cubo de
datos y posteriormente se ejecuta recursivamente sobre cada atributo del cubo, produciendo
tuplas para un conjunto de cuboides cada que se procede con alguno de los n atributos. Más
especı́ficamente, la recursividad del BUC forma arboles de procesamiento con raı́z en cada una
de las n dimensiones o atributos del cubo, por ejemplo, para un cubo de tres dimensiones,
A, B y C, el orden de cálculo de los cuboides serı́a: ALL, A, AB, ABC, AC, B, BC, C. La
principal estrategia de este método es la incorporación de la poda Apriori en cubos iceberg, que
tiene la finalidad de reducir cálculos y consiste en descartar la agregación de particiones que no
cumplen con el umbral de soporte mı́nimo del cubo iceberg. La poda es posible gracias que los
cuboides son calculados de menor a mayor detalle y a la propiedad antimonotonica de algunas
funciones de agregación. Considere de nuevo un cubo de datos de tres dimensiones, A, B, y C,
al iniciarse la recursividad en un atributo como A por ejemplo, los datos en bruto son ordenados
y particionados respecto a A, entonces el algoritmo BUC toma la primera partición y procede
particionando y agregando cada vez con más detalle (respecto a AB y luego respecto a ABC), la
recursividad termina al no satisfacerse el umbral de soporte mı́nimo, es aquı́ donde la propiedad
antimonotonica asegura que si el umbral no fue satisfecho, toda aquella partición más detallada
tampoco lo hará. Sin embargo, este método es susceptible al sesgo y alta dimensionalidad en
los datos. Como se verá más adelante, el BUC ha servido como base para el diseño de varios
métodos paralelos como es el caso de MCBUC presentado en esta tesis.
Star-Cubing: Calculando cubos iceberg mediante integración descendente y ascendente (tı́tulo en inglés, Star-Cubing: Computing Iceberg Cubes by Top-Down and
Bottom-Up Integration)
Star-Cubing [49] es un método desarrollado con la idea de integrar estrategias presentes en
algoritmos ascendentes y descendentes. En lo que respecta a los ascendentes, Star-Cubing utiliza
el principio de poda Apriori introducido por el método BUC [3] y de los algoritmos descendentes
integra la agregación simultanea sobre varias dimensiones (utiliza resultados de agregaciones
51
previas para volver a agregar y ası́ reducir cálculos). Estas estrategias son posibles gracias a
que Star-Cubing utiliza una estructura de datos llamada Star tree, que además proporciona
compresión sin perdida de información. Cada cuboide del cubo de datos es representado por
Star-Cubing mediante un árbol star tree, donde cada nivel del star tree es una dimensión y
los nodos en un nivel del árbol constan de cuatro atributos: el valor de atributo, un valor
agregado, apuntadores a posibles ancestros (cuboides más detallados) y apuntadores a posibles
descendientes (cuboides de menos detalle). El star tree facilita la agregación simultanea ya que
la agregación se realiza a través de los niveles del árbol utilizando los valores de los nodos y
permite también podar la parte de los cuboides que no satisface el umbral de soporte mı́nimo
usando el principio Apriori por que conceptualmente las tuplas están almacenadas en el star tree
de menor a mayor detalle. El desempeño de este método decrece en datos con alta cardinalidad
(alto número de valores por dimensión) ya que la construcción del árbol star tree se vuelve cada
vez más costosa. Star-Cubing no es muy similar a los métodos de esta tesis, sin embargo, es de
importancia mencionarlo ya que es un método bien conocido en la literatura para generación
de cubos completos y de tipo iceberg.
MM-Cubing: Calculando cubos iceberg mediante la factorización del espacio de
lattice (tı́tulo en inglés, MM-Cubing: Computing Iceberg Cubes by Factorizing the
Lattice Space)
Como se ha mencionado, un cubo de datos de D dimensiones se conceptualiza a través de
una estructura de lattice con 2D nodos. Esta lattice a su vez puede visualizarse como una malla
donde todo valor diferente de ALL en una de las D dimensiones converge en un mismo punto,
por tanto, esta malla solo tiene 2D nodos, a esto se le conoce como “espacio de lattice”. En
[43] se introduce el concepto de “Factorización del espacio de lattice” que consiste en dividir
los nodos del espacio de lattice de acuerdo con la frecuencia de los distintos valores unificados
ellos, dando como resultado, la creación de varios sub espacios. No hay intersección entre estos
sub espacios y la suma de todos ellos da por resultado al espacio de lattice original. Con base
en esta factorización, el artı́culo citado propone un método recursivo para cubos completos y
de tipo iceberg llamado MM-Cubing, la idea de este método es tomar en cuenta la densidad de
valores en los datos antes de proceder con la generación de las vistas del cubo, esto se realiza
tomando diferentes heurı́sticas para calcular sub espacios densos (alta densidad de valores) y
dispersos (baja densidad de valores). El conteo para obtener la frecuencia de valores utilizada
para la factorización se realiza en el artı́culo citado a través de un algoritmo llamado Count and
Sort. El estudio experimental presentado en este trabajo sugiere que MM-Cubing obtiene un
alto desempeño en varios tipos de distribuciones de datos superando a BUC [3], Star cubing [49]
entre otros métodos. Sin embargo, este método tiene un alto consumo de memoria ya que las
52
llamadas recursivas a MM-Cubing requieren almacenamiento extra para almacenar estructuras
de datos. MM-Cubing no realiza la generación del cubo de datos de manera similar a los métodos
de esta tesis, sin embargo, se encuentra entre los métodos secuenciales más rápidos conocidos
en la literatura.
2.4.2.
Generación de cubos de datos empleando paralelismo de clusters de PCs
Cálculo de cubos iceberg mediante clusters de PCs (tı́tulo en inglés, Iceberg-cube
Computation with PC Clusters)
En [31], se diseñaron métodos de generación de cubos de datos completos y de tipo iceberg
utilizando paralelismo de grano grueso en clusters de computadoras. La principal aportación de
este trabajo es la evaluación de varios algoritmos paralelos. Más especı́ficamente, este artı́culo
presenta cuatro métodos para generación de cubos de datos, RP, BPP, ASL y PT. Los métodos
RP y BPP son versiones paralelas del algoritmo BUC que explotan la propiedad que tiene el
algoritmo BUC de formar arboles de procesamiento independientes con raı́z en cada atributo del
cubo. La diferencia básica entre los algoritmos RP y BPP está en la forma en que distribuyen
los datos en bruto entre los nodos del cluster de computadoras para calcular el cubo de datos.
RP requiere la duplicación de los datos para cada árbol de procesamiento con raı́z en un cierto
atributo, por lo tanto requiere bastante espacio en disco, además de no ser escalable a clusters
con alto número de nodos ya que solo permite dividir la generación del cubo en n tareas para
un cubo de n dimensiones. En contraste, en BPP los árboles del BUC se procesan uno a uno,
siendo la primera fase para calcular un árbol del BUC particionar los datos en bruto y distribuir
las particiones resultantes entre los nodos del cluster. Lo anterior permite procesar en paralelo
varias particiones pertenecientes a un solo árbol del BUC. Posteriormente, en el artı́culo citado
se introduce al método ASL, que calcula y mantiene los cuboides del cubo de datos en una
estructura de datos conocida como skip list. ASL es un algoritmo descendente ya que comienza
por calcular los cuboides de mayor detalle, lo que le permite compartir operaciones de ordenamiento, por ejemplo, si un procesador ha creado una estructura skip list para generar el cuboide
ABCD, este mismo procesador será encargado de calcular el cuboide ABC sin ordenamientos
adicionales. La distribución de carga en de trabajo en ASL es más uniforme que en RP y BPP
ya que los cuboides son agrupados y distribuidos entre los nodos del cluster simplemente de
acuerdo a sus atributos, por ejemplo, los cuboides ABCD, ABC, AB y A pueden agruparse
porque comparten atributos. Por ultimo, se presenta PT, otro algoritmo paralelo basado en el
BUC pero que mejora la división de carga de trabajo de los algoritmos RP y BPP. PT utiliza los
arboles de procesamiento generados por el BUC en una forma similar al algoritmo RP pero en
53
lugar de asignar un procesador a cada árbol, los árboles son divididos en grupos con el mismo
número de cuboides para su procesamiento en los nodos del cluster. Este trabajo fue unos de los
primeros en presentar la paralelización del proceso de generación del cubo de datos, los métodos
que propone son variaciones del BUC adaptadas a clusters de computadoras, poco similares a
los métodos SPCube y GPUgenCube propuestos en esta tesis, sin embargo, RP y PT son en
cierta medida similares al MCBUC de esta tesis.
Construyendo grandes cubos de datos en paralelo (tı́tulo en inglés, Building Large
ROLAP Data Cubes in Parallel )
En [8] se presenta un método paralelo llamado Parallel-Shared-Nothing-Data-Cube para la
generación total (2n vistas, con n igual al número de dimensiones del cubo) o parcial (subconjunto de las 2n vistas) de cubos de datos en un multiprocesador de arquitectura sin compartimiento,
es decir, un cluster de computadoras donde los nodos de procesamiento constan de discos locales y no comparten acceso a memoria. El método utiliza una técnica de particionamiento que
permite la escalabilidad en clusters de PCs con discos locales conectados a través de un conmutador (en inglés, switch). Considere un conjunto de datos, R, con N registros y d atributos
(D1 , ..., Dd ). Sin perdida de generalidad sea |D1 | ≥ |D2 | ≥ ... ≥ |Dd |, donde |Di | es la cardinalidad para la dimensión Di , 1 ≥ i ≥ d (i.e., el número de distintos valores para la dimensión
Di ). Como entrada, se asume que los datos en bruto, R, con N registros y d dimensiones, se
encuentran igualmente distribuidos a través de p discos. El algoritmo construye una plan de
generación para el cubo de datos que consta de d grupos de cuboides llamados “subcubos”, los
cuales serán ejecutados uno a uno a través del cluster. Esta agrupación en subcubos de realiza
de acuerdo a los atributos de los cuboides, por ejemplo para un cubo de cuatro dimensiones
(A, B, C y D), los d = 4 subcubos y el orden de generación de los cuboides seria, subcubo 1:
ABCD, ABC, AB, A, AC, ABD, ACD, AD, subcubo 2: BCD, BC, B, BD, subcubo 3: CD, C
y subcubo 4: D, ALL. Empleando también la estrategia de reducción de cálculo conocida como
padre mı́nimo, donde se calculan cuboides a partir de otros más detallados en lugar de utilizar
los datos en bruto. Las operaciones de ordenamiento para los subcubos se realizan a través de
un algoritmo de ordenamiento global que funciona a través de los nodos del cluster. ParallelShared-Nothing-Data-Cube es un método reciente de paralelización del proceso de generación
del cubo de datos en clusters de computadoras, la división de la carga de tareas se asemeja
ligeramente al método GPUgenCube presentado en esta tesis, ya que se realiza en base a los
atributos de los cuboides, sin embargo, el flujo general del método es distinto y el grado de
paralelismo del GPUgenCube es superior.
54
2.4.3.
Estrategias de uso de memoria caché en estructuras de datos
y generación de cubos de datos empleando memoria caché
Indexado usando memoria caché para soporte a decisiones en memoria principal
(tı́tulo en inglés, Cache conscious indexing for decision-support in main memory )
En [39] se propusieron mejoras a procesos de indexado y búsqueda en memoria principal
para varias en estructuras de datos como indices hash, árboles de búsqueda binaria, árboles
T, árboles B+, búsqueda por interpolación y búsqueda binaria en arreglos, considerando caracterı́sticas de los CPUs modernos y el comportamiento de la memoria caché. El objetivo del
artı́culo citado es lograr un tiempo de búsqueda inferior al de la búsqueda binaria a través de
la localidad de referencias y el uso de la memoria caché. El artı́culo citado propone una técnica
de indexado llamada “Caché Sensitive Search Trees”. Está técnica almacena un estructura de
directorios en un arreglo ordenado. Los nodos en este directorio tienen un tamaño que ajusta
al tamaño de la caché de la máquina. El directorio es almacenado en un arreglo y sin necesidad de guardar apuntadores a nodos internos; los nodos hijos se pueden encontrar realizando
operaciones aritméticas. Aunque el artı́culo citado no es sobre generación de cubos de datos es
de importancia mencionarlo ya que aborda varias estrategias para utilizar la memoria caché de
procesadores modernos en la aceleración de procesos de indexado en varias estructuras de datos
como las que son utilizadas para almacenar tuplas en el proceso de generación de cubos de
datos.
Cálculo del cubo de datos usando memoria caché en un procesador moderno (tı́tulo
en inglés, Cache-conscious data cube computation on a modern processor )
En [26] se propone un método de generación de cubos llamado CC-Cubing que utiliza la
memoria caché para agilizar el cálculo de cubos de datos en un CPU moderno. Este método
es en esencia una versión optimizada del BUC que mejora su desempeño a través del uso de la
memoria caché. Al igual que el BUC, la ejecución de CC-cubing para un cubo de n dimensiones
está constituida por n árboles de procesamiento independientes. El particionamiento de los
datos en bruto en cada árbol de procesamiento se realiza de manera recursiva, recorriendo
conceptualmente la estructura lattice del cubo en profundidad y amplitud. Posteriormente, con
el fin de aprovechar la tecnologı́a de procesamiento multihilo, se introduce CC-Cubing-SMT,
una versión de CC-cubing que utiliza múltiples hilos para calcular simultáneamente todos los
n árboles generados por el algoritmo BUC original. El artı́culo citado presenta varios métodos
poco similares a los que se proponen en esta tesis, sin embargo, se utilizan hilos y la memoria
caché de un procesador moderno para acelerar la generación del cubo de datos.
55
2.4.4.
Generación en paralelo de cubos de datos usando tecnologı́a
multinúcleo y de GPUs
Cálculo en paralelo del cubo en CPUs y GPUs modernos (tı́tulo en inglés, Parallel
cube computation on modern CPUs and GPUs)
De manera similar a [26], en [51] se diseñan métodos paralelos basados en el BUC [3] utilizando almacenamiento por columnas. En primera instancia se presenta un algoritmo llamado
CC-BUC que mejora la utilización de las lineas de caché de la CPU y la localidad de accesos a
ella. Posteriormente se introduce MC-Cubing una versión multinúcleo de CC-BUC. Este método
es recursivo al igual que el BUC y procede particionando los datos en bruto de menos a más
detalle, el paralelismo en este método se obtiene procesando cada partición usando un núcleo de
la CPU. Por ejemplo, particionando una dimensión A, el número de particiones será igual cardinalidad de A, de manera que a cada uno de los n núcleos de procesamiento le corresponderán
|A|/n particiones. De la misma manera, al procesar la siguiente dimensión, B, habrá aproximadamente |A| × |B| particiones que pueden procesarse separadamente y ası́ sucesivamente. Para
concluir, en el artı́culo citado se comenta el diseña una versión de MC-Cubing para GPUs. Los
métodos que propone el artı́culo citado son variaciones paralelas del BUC poco similares a los
métodos propuestos en esta tesis, sin embargo, también aprovechan el paralelismo y la memoria
caché de GPUs y CPUs multinúcleo.
Generación en paralelo del cubo de datos usando la estructura H-tree en procesadores gráficos (tı́tulo en inglés, Parallel H-Tree Based Data Cubing on Graphics
Processors)
[47] presenta un método llamado ONLINE CUBING para generación de cubos de datos
basado en la estructura de datos conocida como H-tree [19]. Esta estructura de datos permite
calcular cubos de datos parcialmente materializados ası́ como actualización y consulta en lı́nea
de cubos de datos. Se presentan los algoritmos paralelos basados en GPU para operaciones
paralelas de construcción y actualización incremental de la estructura H-tree. En esté trabajo se
utilizan varias primitivas paralelas diseñadas para una GPU (compresión (en inglés, compact),
ordenamiento (en inglés, sort), ordenamiento segmentado (en inglés, segmented sort), recolección
(en inglés, gather ), dispersión (en inglés, scatter ), etc.), mismas que son utilizadas por las
operaciones de la estructura H-tree. El estudio experimental de este trabajo muestra que el
desempeño de los algoritmos de GPU mejoró en comparación con sus contrapartes de CPU.
De manera similar a los métodos GPUgenCube y SPCube propuestos en esta tésis ONLINE
CUBING utiliza varias operaciones paralelas de GPU como subrutinas en la generación del cubo
de datos, sin embargo, el método general es poco similar a GPUgenCube y SPCube.
56
2.4.5.
Similaridades entre trabajos previos y los métodos propuestos
En esta tesis proponen tres métodos paralelos llamados MCBUC, SPCube y GPUgenCube
para generación de cubos de datos completos y de tipo iceberg. Como se verá en el siguiente
capı́tulo, estos tres métodos utilizan algunas estrategias conocidas en la literatura y presentes
en trabajos previos. MCBUC utiliza la poda Apriori por ser una sencilla versión paralela del
BUC [3], SPCube emplea el cálculo de cuboides a partir de otros más detallados y GPUgenCube comparte operaciones de ordenamiento al calcular varios cuboides simultáneamente. Sin
embargo, a diferencia de los métodos previos, exceptuando a [47], los métodos de cubos de datos
presentados en esta tesis constan de un paralelismo de grano fino que se obtiene a través del uso
de un conjunto de operaciones básicas paralelas. Estas operaciones paralelas permiten utilizar
completamente los procesadores de la GPU o CPU multinúcleo al ejecutar subrutinas dentro
del proceso de generación del cubo. Además, los núcleos de la CPU proporcionan a los métodos
presentados en esta tesis un paralelismo de grano grueso que permite construir simultáneamente tuplas para un grupo de cuboides. En [47] también se utilizaron varias subrutinas paralelas
durante la generación del cubo de datos utilizando la estructura de datos conocida como Htree. En contraste, los métodos de esta tesis utilizan arreglos de una dimensión, evitando costos
relacionados con la construcción de estructuras de datos más complejas.
La principal desventaja de los métodos presentados en esta tesis respecto a trabajos como
Star-Cubing [49] y MM-cubing [43] es que no se integra simultáneamente en ellos estrategias
de métodos ascendentes (poda Apriori) y descendentes (compartir operaciones de cálculo y
ordenamiento al generar un grupo de cuboides).
La Tabla 2.2 muestra un resumen de las estrategias usadas por los métodos descritos anteriormente. El indicador “Travesı́a por la lattice” se refiere a la manera en que el método recorre
conceptualmente la lattice del cubo de datos, “Comparte cálculo” indica si el método implementa alguna estrategia para reducir la cantidad de información a agregar en base a agregaciones
previas, “Poda” es un indicador de si el método puede descartar particiones en base al umbral
de soporte mı́nimo de cubos iceberg, “Paralelo” indica si el método utiliza paralelismo de algún
tipo, “Hardware de procesamiento” describe el tipo de hardware de procesamiento utilizado
por el método, por último, la columna “Similaridades con MCBUC, SPCube y GPUgenCube”
menciona el parecido de los métodos previos en comparación con los de está tesis.
57
Método
Travesı́a por
la lattice
Comparte
cálculo
Poda
Paralelo
Hardware de
procesamiento
Similaridades con
MCBUC, SPCube y
GPUgenCube
BUC
Ascendente
No
Si
No
CPU
MCBUC es una versión
paralela del BUC
Star-Cubing
Descendente/
ascendente
Si
Si
No
CPU
No aplica
MM-Cubing
No aplica
Si
Si
No
CPU
No aplica
RP
Ascendente
No
Si
Si
Cluster
MCBUC es una versión
paralela del BUC
similar a RP
BPP
Ascendente
No
Si
Si
Cluster
No aplica
ASL
Descendente
Si
No
Si
Cluster
No aplica
PT
Ascendente
No
Si
Si
Cluster
MCBUC es una versión
paralela del BUC
similar a PT
ParallelSharedNothingData-Cube
Descendente
Si
No
Si
Cluster
GPUgenCube comparte
ordenamientos
de manera similar a
este método
CC-Cubing
Ascendente
No
Si
No
CPU/Caché
No aplica
CC-CubingSMT
Ascendente
No
Si
Si
CPU multi
núcleo/Caché
No aplica
CC-BUC
Ascendente
No
Si
No
CPU/Caché
No aplica
MC-Cubing
Ascendente
No
Si
Si
CPU multinúcleo/Caché
No aplica
ONLINE
CUBING
Descendente
Si
Si
Si
GPU/Caché
SPCube y GPUgenCube
usan paralelismo a
nivel de operación
Tabla 2.2: Métodos previos de generación de cubos de datos, estrategias empleadas. Las similaridades con los métodos
MCBUC, SPCube y GPUgenCube propuestos en esta tesis se resaltan en la última columna de la derecha.
58
Capı́tulo 3
Arquitectura de la solución
Este capı́tulo presenta tres métodos paralelos para construcción de cubos de datos utilizando
almacenamiento en memoria lineal y un conjunto de operaciones básicas denominadas primitivas: recolección, ordenamiento, particiónLocal, particiónCuboide, reducción y reducciónSegmentada. El uso de las primitivas como subrutinas del proceso de generación del cubo de datos
permite aprovechar las ventajas del el paralelismo de CPUs multinúcleo ası́ como el masivo
paralelismo de la GPU.
Para comenzar este capı́tulo y con el fin de dar una idea más clara acerca de las caracterı́sticas
fı́sicas del equipo de cómputo y la GPU utilizados en este trabajo, se describe brevemente el
entorno de hardware para el cual se diseñaron los métodos y primitivas paralelas.
En la segunda parte se presenta un formato vectorial utilizado para almacenar tuplas en
memoria principal. El empleo de este formato es adecuado para reservar espacio en memoria de
la GPU, facilitando la transferencia de información entre memoria RAM y memoria de global de
la GPU ası́ como la aplicación de las primitivas paralelas. Posteriormente se muestran detalles
de diseño de las primitivas junto con algunos ejemplos de aplicación. En esta segunda parte
también se describe el proceso que se lleva a cabo para ordenar tuplas que están en el formato
vectorial, donde intervienen las primitivas recolección y ordenamiento. Para concluir la segunda
parte, se ejemplifica la construcción de un cuboide o vista del cubo de datos, empleando hilo de
CPU y la información proporcionada por las primitivas de partición.
La tercera parte de este capı́tulo se presentan los métodos para generación de cubos de datos
completos y de tipo iceberg: MCBUC, SPCube y GPUgenCube. Se describe la manera en que
agrupan los cuboides o vistas del cubo de datos para generar tareas, flujo de ejecución y la
manera en que se emplean las primitivas de la primera parte para incorporar el paralelismo
dentro de las fases de construcción del cubo de datos.
59
3.1.
Configuración del sistema de cómputo
Para definir un panorama del entorno para el que se diseñaron las primitivas y métodos
para generación de cubos de datos, es de importancia mencionar que se cuenta con una CPU
de 3 núcleos, 4GB de memoria RAM y una GPU modelo Fermi [32] del fabricante NVIDIA
de 336 núcleos (agrupados en 7 multiprocesadores) que cuenta con 1GB de RAM de video.
Recordando la sección del capı́tulo 2 que describió al modelo de programación CUDA, la GPU
utilizada en este trabajo cuenta con una jerarquı́a de memoria de tres niveles. Esta jerarquı́a se
observa conceptualizada en la Figura 3.1. Se considera que la memoria compartida y caché L1
se encuentran en el mismo nivel ya que la latencia de acceso a ellas es equivalente. La memoria
compartida de la GPU es usada en este trabajo como almacén temporal, esta memoria tiene un
tamaño de 48KB en el modelo de GPU que se está utilizando, la memoria caché L1 tiene un
tamaño de 16KB y la memoria caché L2 por su parte almacenar hasta 512KB.
Figura 3.1: Representación de la jerarquı́a de memoria en una GPU Fermi [32] del fabricante NVIDIA. La memoria
compartida y caché L1 se encuentran en el mismo nivel ya que la latencia de acceso a ellas es equivalente.
60
Cada hilo lanzado dentro de la GPU tiene acceso a la memoria global del dispositivo. Sin
embargo, como se mencionó, en CUDA, los hilos son agrupados en bloques y al invocar una
función kernel, cada bloque de hilos tiene acceso a una sección de la memoria de niveles superiores. La ejecución de una función kernel para la GPU se configura normalmente a través de la
especificación de una tupla de parámetros, indicando el número de bloques (malla de bloques),
número de hilos por bloque, configuración de memoria caché, entre otros parámetros, vease la
Tabla 3.1.
threadIdx.x
Variable de identificación de un hilo dentro de un bloque de hilos
blockIdx.x
Variable de identificación de un bloque de hilos dentro de una malla
blockDim.x,y,z
Es una estructura que permite configurar el número de hilos
en un bloque de hasta 3 dimensiones
gridDim.x,y,z
Es una estructura que permite configurar el número bloques de
hilos en una malla de hasta 3 dimensiones
Tabla 3.1: Variables comúnmente usadas como configuración para una función kernel de CUDA
Para el código de GPU realizado en este trabajo se utilizaron bloques de 256 hilos organizados
en una sola dimensión (blockDim.x), este número es potencia de 2 para mejorar el uso del ancho
de banda a memoria [34], además de haberse determinado empı́ricamente que proporciona un
buen desempeño. El número de bloques de hilos es igual al número de multiprocesadores con
que cuenta el modelo de GPU, en este caso 7, ya que aunque los hilos se ejecutan a través de
diferentes núcleos de procesamiento dentro de un mismo multiprocesador, es a nivel de bloque
(multiprocesador) en donde la comunicación es más rápida.
Los datos que darán origen a los cubos se encuentran almacenados en el disco duro en
formato tabular y se leen del disco para depositarlos en la memoria principal (RAM) de la
CPU. A partir de este momento los datos pueden ser manipulados mediante las subrutinas de
los métodos de generación de cubos de datos.
61
3.2.
Primitivas paralelas
En esta sección comienza por describir el formato de almacenamiento en memoria lineal
utilizado para manejar tuplas en memoria RAM y en memoria de GPU. Posteriormente se
describe un conjunto de primitivas paralelas utilizadas como subrutinas dentro de los métodos
de generación de cubos de datos que se describirán posteriormente. Ası́ mismo, esta sección
muestra la manera en que se ordenan tuplas que se encuentra en el formato aquı́ descrito y la
manera en que se construye un cuboide cuando se ejecuta una función de agregación en la GPU.
De manera breve, las primitivas son las siguientes:
Recolección: Re-acomoda los elementos de un arreglo de acuerdo a una colección de ı́ndices.
Ordenamiento: Realiza un ordenamiento ascendente de pares (clave, valor). El ordenamiento se realiza sobre el valor del componente llamado clave.
ParticiónLocal y particiónCuboide: Permiten delimitar grupos de tuplas con valores en
común para facilitar su agregación.
Reducción y reducciónSegmentada: Permiten resolver funciones de agregación como SUM,
MAX y MIN de manera eficiente.
Las primitivas funcionan a través de una GPU, proporcionando un paralelismo de grano fino
mediante sus numerosos procesadores o en el caso de las primitivas de partición, mediante
paralelismo de CPU usando hilos POSIX.
62
3.2.1.
Formato de almacenamiento en memoria lineal
La memoria de la GPU normalmente se reserva de manera lineal, es decir, en arreglos de
una dimensión. Por esta razón y con el fin de maximizar el uso del ancho de banda al acceder a
regiones de memoria RAM y transferirlas a la memoria global de la GPU, las tuplas que darán
origen a las vistas o cuboides del cubo de datos son recuperadas de disco en formato tabular
(véase la Tabla 3.2) y transformadas a un formato secuencial para ser almacenadas en memoria
RAM mediante un vector (véase la Tabla 3.3). Este método de almacenamiento en memoria
lineal es conocido en la literatura como column-major order. En otras palabras, los valores
correspondientes a una columna de una tabla son situados en una sección de un vector, como
se observa en la Tabla 3.3. El almacenamiento en memoria lineal facilita también la aplicación
de las primitivas paralelas durante el proceso de construcción del cubo de datos.
A1
A2
...
a11
a12
:
a1m
a21
a22
:
a2m
... an1
... an2
...
:
... anm
An
Tabla 3.2: Tuplas en formato tabular.
A1
A2
a11 , a12 , ..., a1m
a21 , a22 , ..., a2m
...
An
... an1 , an2 , ..., anm
Tabla 3.3: Tuplas de la Tabla 3.2 en formato de vector.
Este formato permite también transferir de la memoria RAM a la memoria global de la
GPU solo aquellas secciones de los datos que se requieren durante cierta etapa del proceso de
generación del cubo de datos. Por ejemplo, al aplicar una función de agregación como SUM
que se realiza mediante la GPU, solo es necesario transferir de memoria RAM a la memoria
global de la GPU la sección correspondiente al atributo de medida de los datos. En las siguientes
secciones se describe este proceso con más detalle.
63
La Figura 3.2 muestra una representación del proceso de conversión de una tabla con cuatro
tuplas al formato de vector. Los valores de cada columna de la relación se sitúan de manera
contigua.
Figura 3.2: Conversión de cuatro tuplas en formato tabular a un formato vectorial. Cada columna es escrita a una
sección del vector resultante, la primera sección contiene a los elementos de la primera columna, la segunda sección
a los de la segunda y ası́ sucesivamente.
64
3.2.2.
Recolección
Esta primitiva realiza una lectura indexada a los elementos de un arreglo, es decir, su función
es acceder a los elementos de un arreglo almacenado en memoria global de la GPU de acuerdo
a una colección de ı́ndices que llamaremos mapa y acomodarlos en un rango de destino. Un
mapa es un arreglo de enteros que indica las posiciones de otro arreglo a ser accedidas. En otras
palabras, la recolección realiza una lectura aleatoria a un vector y posteriormente realiza una
escritura secuencial. La recolección es conocida en la literatura como gather [20].
Como ejemplo de la recolección, consideremos dos arreglos de cuatro elementos: (3, 1, 2, 0)
y (40, 20, 30, 10), y un arreglo de salida también de tamaño cuatro. Al arreglo (3, 1, 2, 0) le
llamaremos mapa y a (40, 20, 30, 10) vector de valores. La función de la recolección es acceder
el vector de valores respecto a los valores de mapa, esto es, comenzando de izquierda a derecha,
el valor de la posición 0 del mapa es 3, por lo tanto se accederá a la posición 3 del vector de
valores que contiene al valor 10 y se escribirá en la posición 0 del arreglo de salida; el valor de
la posición 1 del mapa es 1, por lo tanto de accederá a la posición 1 del vector de valores que
contiene al valor 20 y se escribirá en la posición 1 del arreglo de salida; el valor de la posición
2 del mapa es 2, por lo tanto se accederá a la posición 2 del vector de valores que contiene al
valor 30 y se escribirá en la posición 2 del arreglo de salida; el valor de la posición 3 del mapa
es 0, por lo tanto se accederá a la posición 0 del vector de valores que contiene al valor 40 y se
escribirá en la posición 3 del arreglo de salida. El arreglo de salida queda (10, 20, 30, 40) y serı́a
entonces una versión re acomodada del vector de valores.
En este trabajo las primitivas recolección y ordenamiento se utilizan como subrutinas para
ordenar tuplas que están almacenadas en el formato lineal que se describió anteriormente, este
proceso de ordenamiento se explicará con detalle en una sección posterior.
Primitiva:
recolección(Re , mapa)
Entrada:
mapa[1, ..., n]: Indices para lectura,
Re [1, ..., n]: Vector de valores a accederse mediante mapa,
Rs [1, ..., n]: Vector de valores de Re accedidos
respecto al orden de los ı́ndices en mapa
Rs [i] = Re [mapa[i]], i = 1, ..., n
Salida:
Función:
Tabla 3.4: Prototipo de la primitiva recolección. La primitiva recibe dos arreglos de enteros, Re y mapa, de tamaño
n como entrada, su función es acceder las posiciones de Re de acuerdo con los valores en mapa, i.e., Re [mapa[i]]
donde i va de 1 a n.
65
La implementación paralela de esta primitiva se realizó utilizando bloques de hilos de GPU.
Cada bloque de hilos accede una región del vector a recolectar cuyo tamaño es equivalente a
su número de hilos y utiliza la memoria compartida de la GPU para almacenar temporalmente
los elementos accedidos. Cada hilo de un bloque accede a una posición del vector de acuerdo al
indice del mapa que le corresponde.
La Figura 3.3 representa la ejecución de la primitiva recolección sobre un vector de ocho
elementos a través de dos bloques de hilos. Como puede observarse, el vector de entrada es
indexado de acuerdo a los valores del mapa, el resultado es un nuevo vector re acomodado de
acuerdo a estos indices. El mapa y los valores de entrada están almacenados fı́sicamente en la
memoria global de la GPU mediante dos arreglos, los bloques de hilos de la GPU leen los valores
de ambos arreglos para escribir la salida a un tercer arreglo que también reside en la memoria
global de la GPU.
Figura 3.3: Representación la primitiva recolección sobre un vector de ocho elementos. El valor de la posición 0 del
mapa (es el 1, de izquierda a derecha) indica que se accederá la posición 1 del vector de valores que este caso contiene
el valor 21 el cual irá en la posición 0 de la salida; el valor de la posición 1 del mapa (es el 2) indica el acceso al valor
9 que irá en la posición 1 de la salida; el valor de la posición 2 del mapa (es el 0) indica el acceso al valor 10 que
irá en la posición 2 de la salida; el proceso continua hasta acceder los 8 elementos de la entrada.
66
3.2.3.
Ordenamiento
El ordenamiento de datos es una operación que ha sido bien estudiada en la teorı́a de
algoritmos [24] y es de suma importancia en el ámbito de construcción de cubos de datos ya
que se requiere agrupar tuplas relacionadas. En el cálculo de cubos la agregación se realiza
sobre tuplas (o celdas) que comparten el mismo conjunto de valores de dimensión. Por tanto
es importante ordenar y agrupar las tuplas para facilitar el cálculo de los agregados y debe
realizarse de forma eficiente ya que algunos algoritmos emplean gran parte del tiempo en este
tipo de operaciones.
La función de la primitiva de ordenamiento es simplemente ordenar de manera ascendente un
conjunto de pares de valores. Es decir, esta primitiva toma como entrada a dos arreglos de una
dimensión que residen en memoria global de la GPU, el primero contiene un conjunto de valores
ordenar y el segundo contiene un conjunto de valores que serán utilizados como referencia para
ordenar a los valores del primero. Llamaremos claves a los valores del arreglo que será usado
como referencia. Por ejemplo, considere como entrada al arreglo de valores: (3, 2, 6, 10, 15, 12)
y a un arreglo de referencia (claves): (1, 0, 3, 2, 5, 4), el resultado del ordenamiento por clave
de estos dos arreglos es: (2, 3, 10, 6, 12, 15) y (0, 1, 2, 3, 4, 5).
En este trabajo, las primitivas de ordenamiento y recolección tienen el propósito de funcionar
como subrutinas en el proceso de ordenar tuplas almacenadas en el formato vectorial descrito
en la Figura 3.3. Este proceso se describirá en una sección posterior.
El algoritmo de ordenamiento paralelo empleado en esta tesis funciona mediante la GPU y
fue implementado de manera similar a que se presenta en [41]. Este algoritmo de ordenamiento
está basado en el bien conocido radix sort. El radix sort es uno de los algoritmos de ordenamiento
más antiguos y mejor conocidos, en máquinas secuenciales es con frecuencia también uno de los
más eficientes cuando se ordena claves pequeñas. El algoritmo asume que las claves son números
de d dı́gitos y ordena sobre un dı́gito de las claves a la vez, de menos a más significativo. Para
un tamaño fijo de clave d la complejidad de ordenar n registros de entrada será O(n) [24].
Primitiva:
ordenamiento(Re , claves)
Entrada:
Re [1, ..., n]: Vector de valores a ordenar,
claves[1, ..., n]: claves de ordenamiento
Re [1, ..., n]: Relación ordenada respecto a claves
Radix sort paralelo basado en histogramas
Salida:
Función:
Tabla 3.5: Prototipo de la primitiva ordenamiento. Re y claves son arreglos de una dimensión. Los valores en claves
son utilizados como referencia para ordenar los valores de Re .
67
La Figura 3.4 muestra una representación del ordenamiento por clave del arreglo de enteros
antes mencionado.
Figura 3.4: Ejemplo de ordenamiento por clave de un vector de seis elementos. El vector de claves sirve como referencia
para ordenar un vector de valores.
Antes de pasar a la paralelización del radix sort conviene explicar como funciona en máquinas
secuenciales. Como ejemplo consideremos la siguiente lista de enteros: (170, 45, 75, 90, 802, 2,
24, 66). Dividiendo cada elemento de esta lista de números en dı́gitos decimales se tiene que los
números más grandes de la lista a ordenar constan de tres dı́gitos (170, 802), entonces, el radix
sort requiere aplicar tres pasadas para ordenar la lista completa. Como la base de los dı́gitos es
10 se requieren a lo más 10 almacenes temporales o buckets para ordenar los dı́gitos.
1. Comenzando por ordenar el dı́gito menos significativo se tiene:
170, 090, 802, 002, 024, 045, 075, 066
bucket 0: 170, 090; bucket 2: 802, 002; bucket 4: 024; bucket 5: 045, 075; bucket 6: 066
2. Ordenando el dı́gito siguiente se obtiene:
802, 002, 024, 045, 066, 170, 075, 090
bucket 0: 802, 002; bucket 2: 024; bucket 4: 045; bucket 6: 066; bucket 7: 170, 075; bucket
9: 090
3. Ordenando por el dı́gito más significativo:
002, 024, 045, 066, 075, 090, 170, 802
bucket 0: 002, 024, 045, 066, 075, 090; bucket 1: 170; bucket 8: 802
Nótese que cada uno de los pasos anteriores requiere solo una pasada sobre los datos, ya que
cada elemento se coloca en la posición correcta de un bucket sin tener que comparar con otros
elementos.
68
El algoritmo de ordenamiento utilizado dentro de las d pasadas del radix sort es normalmente
un ordenamiento por cuentas (en inglés, counting sort) [42], que como se mostró en las pasadas
del ejemplo anterior, trabaja sobre un dı́gito de las claves a ordenar.
Cada dı́gito base 2b es una cadena de b bits dentro de la clave. Para ordenar un dı́gito dado
de cada clave en una cierta pasada del radix sort, se calcula el número de claves cuyos dı́gitos
son más pequeños más el número de claves que ocurrieron previamente y que tuvieron el mismo
dı́gito en la secuencia. Este será el indice de salida en el que el elemento será escrito, al cual se le
referirá como el rango (en inglés, rank ) del elemento. Por ejemplo, consideremos cuatro dı́gitos
decimales de clave: (0, 2, 3, 3), el primer dı́gito (0) de izquierda a derecha irá en la posición
0 de la salida por que no hubo ocurrencias de dı́gitos de clave iguales o más pequeños en la
secuencia a ordenar, el segundo dı́gito (2) va en la posición 1 de la salida por que antes de el
hubo un dı́gito más pequeño (0), el tercer dı́gito (3) va en la posición 2 de la salida ya que antes
de el hubo dos ocurrencias de claves más pequeñas (0, 2), el cuarto dı́gito a ordenar (2) va en
la posición 3 de la salida ya que antes de el hubo dos ocurrencias de dı́gitos más pequeños (0,
2) y una de un dı́gito igual (3). Habiendo calculado el rango de cada elemento, se completa la
pasada dispersando los elementos arreglo de salida.
El ordenamiento de cada dı́gito de menos a más significativo garantiza que el radix sort
dejará la secuencia de claves correctamente ordenada después de completar las d pasadas. Más
en concreto, el Algoritmo 2 muestra el pseudocódigo con los detalles acerca de la ejecución del
ordenamiento por cuentas dentro de cada pasada del radix sort, tomando un dı́gito (base 10) de
cada clave. Las lı́neas 1-4 muestran el cálculo de un histograma que registra las ocurrencias de
elemento iguales en la secuencia a ordenar. Las lı́neas 6-10 muestran el ciclo donde se calcula el
ı́ndice de salida correcto para cada elemento a ordenar usando el histograma de las lı́neas 1-4.
A la operación efectuada en las lı́neas 6-10 de este pseudocódigo se le conoce comúnmente con
el nombre de suma de prefijos o suma acumulativa.
69
Algoritmo 2 ordenamiento por cuentas
Entrada:
Dı́gitos a ordenar
Salida:
Dı́gitos en orden ascendente
1: Reservar un arreglo conteo[k] y asignar cero a cada posición
. k es la base de los dı́gitos
2: Para cada dı́gito x hacer
. Calcular histograma
3:
Incrementar conteo[x]
4: Fin Para
5: total ← 0
6: Para i ← 0; i < k; i + + hacer . Suma de prefijos: calcula el indice inicial para cada dı́gito
7:
cuentaAnterior ← conteo[i]
8:
conteo[i] ← total
9:
total ← total + cuentaAnterior
10: Fin Para
11: Reservar un arreglo salida[n]
. n es el número de dı́gitos a ordenar
12: Para cada dı́gito x hacer
. Copia los dı́gitos en orden a un arreglo de salida
13:
salida[conteo[x]] ← x
14:
Incrementar conteo[x]
15: Fin Para
Pasando a la implementación paralela del ordenamiento, varios estudios sugieren que el radix
sort está entre los algoritmos de ordenamiento más sencillos para implementar en paralelo y es
tan eficiente como algunos algoritmos más sofisticados como el sample sort [5, 11].
En esencia, la paralelización del radix sort mediante la GPU consiste en dividir la secuencia
de entrada para cada pasada del radix sort en secciones que son repartidas entre un grupo de
bloques hilos de GPU. A cada una de estas secciones de la entrada le llamaremos mosaico.
En cada pasada del radix sort paralelo, los elementos de cada mosaico son ordenados localmente mediante un bloque de hilos de la GPU, cada bloque de hilos consta de un histograma
local que permite ordenar localmente a los elementos del mosaico. Posteriormente una operación
de sumas de prefijos a través de los histogramas de los todos los bloques de hilos permite determinar las posiciones globales que los dı́gitos deben ocupar en el arreglo de salida. Un ejemplo
se presenta a continuación.
70
Como ejemplo del radix sort paralelo, considere de nuevo la lista de enteros (170, 45, 75, 90,
802, 2, 24, 66) mencionada anteriormente y dos bloques de hilos. A cada uno de estos bloques
le será asignada una sección de la lista a ordenar. Utilizando por claridad del ejemplo dı́gitos
decimales, la primera pasada del radix sort paralelo quedarı́a como se observa en la Figura 3.5.
Cada bloque de hilos calcula un histograma de las ocurrencias del dı́gito considerado actualmente
en su respectiva sección de la entrada. En esta primera pasada se evalúa el primer dı́gito de
las claves. El histograma del bloque 0 registra la ocurrencia de dos dı́gitos 0 (por 179 y 90) y
dos dı́gitos 5 (por 45 y 75). El histograma del bloque 1 registra la ocurrencia de dos dı́gitos
2 (por 2 y 24), de un dı́gito 4 (por 24) y de un dı́gito 6 (por 66). Los histogramas anteriores
permiten ordenar localmente a los elementos de la sección de la entrada que le corresponde al
bloque de hilos. Para determinar las posiciones globales que los elementos de los mosaicos deben
ocupar en el arreglo de salida se realiza una operación de suma de prefijos a través de todos los
histogramas. Esta suma de prefijos es una suma acumulativa de los conteos de cada dı́gito de
los histogramas. Por ejemplo, en la Figura 3.5 el resultado de la suma de prefijos es 0, 2, 4, 5
y 7 ya que se registraron dos dı́gitos 0 (2), dos dı́gito 2 (4), un dı́gito 4 (5) y dos dı́gitos 5 (7).
Los resultados de la suma de prefijos se observan en las Figuras 3.5, 3.6 y 3.7 apuntando con
flechas a la posición correspondiente del arreglo de salida.
Figura 3.5: Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 1 de 3. La ilustración muestra
el ordenamiento de la lista de números respecto al primer dı́gito decimal.
La Figura 3.6 muestra dos bloques de hilos ordenando la salida producida en la Figura 3.5.
La segunda pasada del radix sort procede con el siguiente dı́gito de los números a ordenar, en
este ejemplo la segunda pasada procede con las decenas.
71
Cada bloque de hilos re-calcula su propio histograma con el nuevo orden de los elementos
que produjo la primera pasada (Figura 3.5).
Figura 3.6: Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 2 de 3. La ilustración muestra
el ordenamiento de la lista de números respecto al segundo dı́gito decimal.
Para concluir con el ordenamiento de la lista de números, la Figura 3.7 muestra dos bloques
de hilos ordenando la salida producida en la Figura 3.6. La tercera pasada re-acomoda los
elementos de acuerdo al dı́gito más significativo de las claves, en este caso las centenas y deja
correctamente ordenada la secuencia de entrada.
Figura 3.7: Ejemplo radix sort paralelo para ordenar una lista de ocho números: pasada 3 de 3. La ilustración muestra
el ordenamiento de la lista de números respecto al tercer dı́gito decimal.
72
En el ejemplo anterior del radix sort, los números de la lista a ordenar fueron divididos en
dı́gitos decimales, pero en la implementación los números son manipulados en su representación
binaria, evaluando grupos de b bits a la vez.
Una manera sencilla de paralelizar las pasadas del radix sort es ordenando las claves usando
1 bit a la vez, esto se realizó en [4] bajo el nombre de “operación de división” (en inglés, split
operation), sin embargo, esto no es particularmente eficiente ya que para claves de 32 bits
habrı́a 32 pasadas del radix sort para re-ordenar la secuencia completa. Una manera lógica de
solucionar esto es considerar más de b = 1 bits por pasada. La idea de esta implementación
es hacer uso eficiente del ancho de banda de la memoria minimizando el número de escrituras
dispersas a memoria global de la GPU y maximizando la coherencia de las dispersiones. La
división de los datos en bloques y un tamaño de dı́gito b > 1 logra aumentar el uso del ancho
de banda de acceso a memoria global de la GPU. La minimización de escrituras dispersas a
memoria global de la GPU se logra utilizando la memoria compartida de la GPU para ordenar
los bloques de datos respecto al dı́gito base 2b actual. Esta estrategia convierte las escrituras
dispersas a memoria externa en escrituras dispersas en memoria compartida.
Cada pasada del radix sort se implementó en cuatro fases. A falta de un mecanismo de
sincronización entre bloques de hilos en la plataforma CUDA [33], cada una de las siguientes
fases corresponde a la invocación de una función kernel distinta:
1. Cada bloque carga y ordena su mosaico en memoria caché usando b iteraciones, es decir,
una iteración por cada división de 1 bit.
2. Cada bloque escribe su histograma de 2b entradas de dı́gitos y el mosaico ordenado a
memoria global.
3. Se realiza una suma de prefijos sobre la tabla de p×2b histogramas, almacenada fı́sicamente
en formato por columnas (column-major order ), a fin de calcular los desplazamientos
globales de dı́gitos [11, 50]. Véase la Figura 3.8.
4. Usando los resultados de la suma de prefijos, cada bloque de hilos copia sus elementos a
la posición de salida correcta (en preparación para la siguiente pasada si es que la hay).
Entonces, con p bloques de hilos y b bits, se tendrán p buckets con 2b entradas, donde cada hilo
de GPU obtendrá el rango de un conjunto de elementos en cada pasada del radix sort.
73
Figura 3.8: Conceptualización de los histogramas del radix sort paralelo. Cada bloque de hilos de GPU consta de
un histograma para ordenar una sección de la secuencia de entrada. En cada pasada del radix sort se realiza una
operación de suma de prefijos global a través de todos los histogramas para determinar la posición global de los
elementos ordenados por cada bloque de hilos en el arreglo de salida.
Se determinó empı́ricamente la utilización de un tamaño b = 4 para la implementación de
las pasadas, ya que utilizar un número más pequeño implica más pasadas y un número mayor
reduce la coherencia de los accesos a memoria.
74
3.2.4.
Proceso de ordenamiento de tuplas
En este trabajo, el proceso de ordenamiento de una relación en formato de vector se realiza mediante la ejecución de las primitivas paralelas de recolección y ordenamiento. Con este
método, los valores de los atributos o dimensiones de una relación no se ordenan directamente,
en su lugar, para ordenar la relación se construye un vector de enteros cuyos valores permiten
determinar las posiciones que los valores de cada atributo las tuplas tomarı́an al estar ordenados
respecto a una cierta combinación de atributos. La primitiva de recolección utiliza este vector
como mapa para dar el orden deseado a las tuplas que se encuentran almacenadas en formato
de vector. Por ejemplo, para ordenar las tuplas de una relación con tres atributos, A, B y C,
respecto a la combinación ABC se necesita primeramente construir un vector de enteros que
será utilizado como mapa por la primitiva de recolección para determinar las nuevas posiciones
de los valores de A, B y C. La recolección de los valores de A, B y C usando este vector como
mapa, dejará las tuplas ordenadas respecto a la combinación ABC.
Ahora bien, el tamaño de este vector o mapa en el contexto de la recolección, es el número
de tuplas a ordenar e inicialmente es una secuencia de enteros que va desde cero hasta el número
de tuplas menos uno. Para obtener la versión final de este mapa con el que los valores de cada
atributo serán re-acomodados, habrá que realizar un proceso donde se aplica la recolección a los
valores de cada atributo de los datos usando este vector como mapa y luego se ordena al mismo
mapa usando como clave a los valores que han sido previamente recolectados. Este proceso
de recolección-ordenamiento se realiza comenzando por el atributo menos significativo respecto
al que se desea ordenar las tuplas. Por ejemplo, retomando el ejemplo anterior de ordenar un
conjunto de tuplas respecto a la combinación de atributos ABC, para construir el mapa que
dará el ordenamiento final a las tuplas primero habrá que recolectar el atributo C usando al
mapa y luego ordenar al mapa usando los valores de la nueva versión de C como claves, después
se procede a realizar la recolección B usando al mapa y se ordena de nuevo al mapa usando
como clave a la nueva versión del atributo B, por ultimo se realiza la recolección del atributo
A usando al mapa y se ordena al mapa usando como clave a la nueva versión del atributo
A. Este proceso de recolección-ordenamiento no cambia de lugar a los valores de los atributos
del vector de tuplas, ya que se utiliza un vector temporal para almacenar los resultados de
la recolección de cada atributo. Cabe remarcar que siempre es necesario realizar este proceso
de recolección-ordenamiento para todo atributo de los datos aunque solo se requiera ordenar
respecto a algunos, por ejemplo, al ordenar tuplas respecto a un solo atributo de una relación con
3 atributos habrá que registrar en el mapa también los valores de los otros dos no importando
el orden en que se ejecuten.
75
Para concluir con el ordenamiento de tuplas, este vector o mapa es aplicado a cada uno de
los segmentos del vector de tuplas que contiene los valores de cada atributo, obteniendo el nuevo
orden de las tuplas.
Este método para ordenamiento de tuplas se basa en el orden lexicográfico, el cual es una
generalización de la forma en que el orden alfabético de las palabras se basa en el orden alfabético
de las letras que lo componen. Dados dos conjuntos parcialmente ordenados A y B, el orden
lexicográfico sobre el producto cartesiano de A × B se define como (a, b) ≤ (a0 , b0 ) si y solo
si a < a0 o (a = a0 ) y (b ≤ b0 ). El resultado es un orden parcial. Si A y B están totalmente
ordenados entonces el resultado también es un orden total.
De manera más general, se puede definir el orden lexicográfico sobre el producto cartesiano de
n conjuntos ordenados, sobre el producto cartesiano de una familia infinita contable de conjuntos
ordenados y sobre la unión de dichos conjuntos. Un ejemplo detallado aparece a continuación.
Antes de comenzar con el ejemplo, observe que la la parte superior de la Figura 3.9 muestra
un vector con 4 tuplas: (2, 2, 1), (1, 1, 1), (3, 1, 2) y (5, 1, 1) de tres dimensiones: A, B y C. La
parte inferior de la Figura 3.9 muestra el mismo vector ordenado respecto a la combinación de
atributos ABC, es decir, los valores del atributo A varı́an lentamente, los valores de B varı́an
un poco más rápido y los valores de C varı́an más rapidamente.
Figura 3.9: Ordenamiento de cuatro tuplas respecto a la combinación de atributos ABC. Las tuplas a ordenar son (2,
2, 1), (1, 1, 1), (3, 1, 2) y (5, 1, 1). Esto se muestra en el vector superior. El resultado de ordenar las tuplas a la
combinación ABC es: (1, 1, 1), (2, 2, 1), (3, 1, 2) y (5, 1, 1). Esto se muestra en el vector inferior.
Como ejemplo del ordenamientos de tuplas, considere el vector de la Figura 3.9; el proceso
de construcción del mapa para ordenar las tuplas de esta ilustración se muestra en la Figura
3.10. Como se ha mencionado, inicialmente el mapa es una secuencia incremental de enteros
cuyo tamaño es el número de tuplas a ordenar, en este caso es (0, 1, 2, 3).
76
Los valores del mapa se ordenan usando a los valores en cada una de los atributos o dimensiones como claves, este proceso registra mediante el mapa las permutaciones en los valores de
cada atributo de los datos. Se comienza con la dimensión menos significativa en la combinación
a tomarse en cuenta, en este caso es C. Al terminar con una dimensión, el mapa es utilizado para
recolectar los elementos de la siguiente dimensión, en este ejemplo es B y se procede nuevamente
a ordenar usando el siguiente conjunto de claves (valores de B). Este proceso se repite hasta
terminar con todas las dimensiones de los datos, en este caso solo queda A.
Figura 3.10: Generación de un mapa para ordenar las tuplas de la Figura 3.9 respecto a la combinación ABC. El
mapa es inicialmente una secuencia ascendente de enteros cuyos valores van permutando debido a una serie de
ordenamientos por clave. Las claves son los valores de un cierto atributo de los datos en bruto y se inicia por ordenar
respecto al atributo menos significativo, en este caso C. La versión final del mapa permite usar a la recolección para
re-acomodar los valores de cada sección del vector de tuplas (datos en bruto) correspondiente a un atributo, dejando
las tuplas ordenadas respecto a la combinación que se consideró, en este caso ABC. Véase la Figura 3.11.
77
Para terminar con el ordenamiento de las tuplas, la fase final es aplicar la primitiva de recolección a cada una de las dimensiones de los datos usando el mapa que se construyó registrando
las permutaciones en los valores de cada atributo o dimensión de los datos. La Figura 3.11
muestra la ejecución de esta ultima fase sobre las tuplas de la Figura 3.9 usando la versión final
del mapa mostrado en la Figura 3.10.
Figura 3.11: Fase final del ordenamiento de las tuplas en la Figura 3.9. Se usa el mapa de la Figura 3.10 para aplicar
la recolección a cada sección del vector de tuplas que corresponde a un atributo de los datos. Esta última fase deja a
las tuplas de la Figura 3.9 ordenadas respecto a la combinación ABC.
Los detalles generales del proceso de ordenamiento de tuplas se dan en el Algoritmo 3. La
función actualizarPermutación() permuta los valores del vector que será usado como mapa en
la fase final del ordenamiento de tuplas, esta función no modifica los valores del vector que
contiene las tuplas, ya que se usa un vector auxiliar para almacenar temporalmente los valores
de un cierto atributo. Los marcadores inicio y f in son utilizados para delimitar una sección
correspondiente a cierto atributo en el vector de tuplas. La fase final es realizada por la función
aplicarPermutación(), aplicando la primitiva de recolección a las secciones del vector de tuplas
usando como mapa al vector construido por actualizarPermutación().
78
Algoritmo 3 ordenarTuplas
Entrada:
global ntuplas: Número de tuplas a ordenar
global ndimensiones: Número de dimensiones de los datos
datos[ntuplas ∗ ndimensiones]: Vector con las tuplas a ordenar
orden[ndimensiones]: Numeración de los atributos de la relación a ordenar, de más a menos
significativo (la posición en el arreglo lo indica)
permutación[ntuplas]: Arreglo para registrar permutaciones y re-acomodar valores mediante
la primitiva de recolección. Inicialmente es una secuencia de enteros (0, 1, ..., ntuplas − 1)
Salida:
Vector de tuplas ordenadas respecto a una combinación de atributos.
1: Función actualizarPermutación(datos, inicio, f in, permutación)
2:
temporal[ntuplas]
. Almacén temporal
3:
temporal ← recolección(datos[inicio, ..., f in], permutación)
4:
ordenamiento(permutación, temp)
5: Fin Función
6: Función aplicarPermutación(datos, inicio, f in, permutación)
7:
temporal[ntuplas]
. Almacén temporal
8:
temporal ← datos[inicio, ..., f in]
9:
datos[inicio, ..., f in] ← recolección(temporal, permutación)
10: Fin Función
11: Para i ← ndimensiones − 1; i ≥ 0; i − − hacer . Recolección y ordenamiento a partir del
atributo menos significativo
12:
inicio ← orden[i] ∗ ntuplas
. Inicio de la sección de valores de un atributo
13:
f in ← ntuplas + (orden[i] ∗ ntuplas)
. Final de la sección de valores de un atributo
14:
actualizarPermutación(datos, inicio, f in, permutación)
15: Fin Para
16: Para i ← ndimensiones − 1; i ≥ 0; i − − hacer
. Obtiene el orden final de las tuplas
17:
inicio ← orden[i] ∗ ntuplas
18:
f in ← ntuplas + (orden[i] ∗ ntuplas)
19:
aplicarPermutación(datos, inicio, f in, permutación)
20: Fin Para
79
3.2.5.
ParticiónLocal y particiónCuboide
En los métodos de cubos de datos que se presentarán más adelante, la partición es una etapa
posterior al ordenamiento de tuplas y que va seguida de la agregación cuando se construye un
cuboide o vista del cubo. Es un proceso que determina los valores que formarán las tuplas
de un cierto cuboide y delimita los grupos de datos que producirán los respectivos valores
agregados. En la Figura 3.12 se observa una tabla con cinco tuplas siendo particionada respecto
a la combinación de atributos ABC, si adicionalmente se aplicara la función de agregación
COUNT(*) sobre los grupos de esta relación, los resultados serı́an 1, 1, 1 y 2 respectivamente
(iniciando de arriba hacia abajo).
Figura 3.12: Particionamiento de una tabla con cinco tuplas respecto a la combinación de atributos ABC.
La partición como normalmente se lleva a cabo, es un proceso secuencial, donde se toma un
conjunto de tuplas ordenadas respecto a una combinación de atributos y se procede a revisar los
valores de los atributos para delimitar segmentos a agregarse. El modelo de partición aquı́ utilizado se diseño mediante dos fases, esto con el fin de reutilizar los marcadores producidos por
la primera fase de la partición (particiónLocal) para particionar varios cuboides que comparten
los mismos atributos y ası́ reducir el número de pasadas a los datos en bruto. Por ejemplo, si
particionamos respecto a la combinación de atributos ABC, los mismos marcadores servirán
para particionar respecto a las combinaciones AB y A.
80
Recordando del formato de datos aquı́ utilizado que los datos de la relación base se encuentran almacenados en memoria RAM mediante un vector, es claro que los valores correspondientes
a un atributo de la relación base corresponden a una sección de dicho vector. La primera fase
de la partición se encarga de asignar un hilo de CPU (no de GPU, abajo se explicará porqué) a
cada sección para ser particionada. Más especı́ficamente, cada hilo recorre una sección del vector
de tuplas y obtiene un conjunto de marcadores (marcadores relativos a la sección del vector de
tuplas) que registran cambios en los valores. De manera que las dimensiones de la relación base
son particionadas en paralelo, tal es la función que realiza la primitiva particiónLocal.
Primitiva:
particiónLocal(Re , combinación, Rs )
Entrada:
Salida:
Función:
Re : Vector de tuplas a particionar, combinación: Atributos a considerarse
Rs : Vector de marcadores de segmentos por cada atributo en combinación
Particionamiento paralelo de los atributos en combinación
Tabla 3.6: Prototipo de la primitiva particiónLocal. Su función es recorrer en Re las secciones correspondientes a
los valores de cada atributo de la combinación respecto a la cual se desea particiónar, obteniendo un conjunto de
marcadores por cada atributo. Los marcadores son las posiciones de Re donde se registró un cambio de valor.
Una vez que la primitiva particiónLocal obtiene los marcadores de segmentos por cada
dimensión considerada para la construcción de un cuboide cuboide o vista del cubo, la segunda
fase realiza una unión con dichos marcadores (eliminando repetidos), i.e., {SD1 ∪SD2 ∪...∪SDm }
donde SDi es un conjunto de marcadores para una dimensión y m es el número de dimensiones del
cuboide, este es el trabajo de la primitiva particiónCuboide. El resultado de las dos fases obtiene
un conjunto de marcadores que servirá para determinar los grupos de datos a ser agregados a
partir de la relación base y construir las tuplas en el cuboide correspondiente.
Primitiva:
particiónCuboide(Re , combinación, Rs )
Entrada:
combinación: Atributos a considerarse,
Re : Vector de marcadores de segmentos por cada atributo en combinación
Rs : Vector de marcadores de segmentos tomando en cuenta todos los
atributos en combinación
Unión de marcadores de segmentos de los atributos en combinación
Salida:
Función:
Tabla 3.7: Prototipo de la primitiva particiónCuboide. La función de esta primitiva es realizar una operación de unión
con los conjuntos de marcadores de cada atributo de la combinación respecto a la que se va a particionar. Los
marcadores se encuentran almacenados en un arreglo Re que para esta fase puede contener marcadores para otros
atributo no incluidos en la combinación. El resultado es un arreglo de marcadores Rs tomando en cuenta solo a los
atributos de la combinación.
81
La Figura 3.13 muestra de un esquema del proceso de partición, comenzando por la asignación de los datos en memoria. Como puede observarse, cada dimensión de los datos es particionada localmente respecto a cada uno de los atributos en la combinación a tomarse en cuenta.
Por ejemplo, si se requiere particionar los datos respecto a la combinación de atributos ABC,
es necesario primero particionar con respecto a A posteriormente con B y luego con C. En una
segunda fase de partición, se realiza una operación de unión entre los segmentos obtenidos en
la partición local para obtener los grupos respecto a la combinación deseada.
Figura 3.13: Representación del proceso de partición de un grupo de tuplas en formato de vector respecto a la
combinación de atributos ABC.
La razón de emplear hilos de CPU (POSIX) en lugar de hilos de GPU en las primitivas de
partición corresponde a que normalmente el número de dimensiones del cubo de datos no supera
las decenas, además, dado que la partición de una sección del vector de tuplas es un proceso
secuencial, los núcleos de la CPU son más adecuados en este tipo de tareas.
82
Como ejemplo consideremos la ilustración de la Figura 3.14, se tiene una tabla con cinco tuplas y con tres dimensiones (A, B y C) convertida a formato de vector, para construir el cuboide
ABC, la primera fase de la partición se encargarı́a de obtener los marcadores correspondiente a
los atributos A, B y C, es decir, tres hilos se encargarı́an de registrar cambios en los valores de
las secciones correspondientes a A, B y C. Posteriormente la segunda fase realizarı́a una unión
que determinarı́a los marcadores para construir el cuboide ABC. En este caso, comenzando por
la posición cero, los marcadores son: para el atributo A {0, 1, 3}, para el atributo B {0, 1, 3} y
para el atributo C {0, 2}. Entonces, realizando una unión entre estos conjuntos el resultado es
{0, 1, 2, 3}, es decir, hay cuatro particiones en los datos respecto a la combinación de atributos
ABC (0-1, 1-2, 2-3 y 3-4).
Figura 3.14: Ejemplo del proceso de partición de un vector con cinco tuplas respecto a la combinación de atributos
ABC. ParticiónLocal se encarga de lanzar un hilo de CPU para particionar cada atributo que se requiera (en este
caso A, B y C). ParticiónCuboide realiza la unión con los marcadores producidos por particiónLocal (en este caso,
marcadores de A, B y C). El resultado de particiónCuboide es un conjunto de marcadores que permite la generación
de un cuboide o vista del cubo (en este ejemplo son para el cuboide ABC).
La información proporcionada por el particionamiento es también suficiente para efectuar
la función de agregación COUNT empleando operaciones aritméticas con los marcadores. Por
ejemplo, utilizando los marcadores de la Figura 3.14, es sencillo determinar que las particiones
del cuboide ABC contienen 1 − 0 = 1, 2 − 1 = 1, 3 − 2 = 1 y 5 − 3 = 2 tuplas respectivamente.
83
3.2.6.
Reducción y reducciónSegmentada
Durante la construcción de un cuboide o vista del cubo de datos a partir de datos de gran
volumen, funciones de agregación como MAX, MIN, SUM y AVG producen altas cantidades de
operaciones aritméticas y lógicas. En este trabajo se utilizaron los algoritmos conocidos como
reducción (en inglés, reduction) [37] y reducciónSegmentada (en inglés, segmented reduction)
[52] para producir los valores agregados correspondientes a un cuboide o vista del cubo. Como
las tuplas para generar el cubo de datos se encuentran almacenadas en un arreglo de una
dimensión, las primitivas reducción y reducciónSegmentada permiten agregar las secciones que
corresponden a atributos de medida, esta sección explica su funcionamiento.
La reducción es una clase de algoritmo paralelo que toma una entrada de datos O(N ) y genera
un resultado O(1) calculado mediante un operador binario asociativo ⊕. La reducción en GPUs
fue presentada por primera vez en [37]. Algunos ejemplos de operaciones que pueden resolverse
mediante esta operación son: MAX, MIN, SUM, suma de cuadrados, AND, OR y el producto
cartesiano de dos vectores. Como el operador binario es asociativo, las O(N) operaciones para
P
calcular la reducción pueden ejecutarse en cualquier orden. e.g.,
ai = a0 ⊕ a1 ⊕ a2 ⊕ a3 ⊕ a4 ⊕
a5 ⊕ a6 ⊕ a7 . La Figura 3.15 muestra dos opciones de procesar un arreglo de ocho elementos.
Figura 3.15: Reducción de un vector de ocho elementos.
La implementación serial se muestra solo para contrastar, en ese caso solo se necesita una
unidad de ejecución para aplicar el operador ⊕, pero el desempeño es pobre ya que se necesitan
7 pasos para completar el cálculo. El otro modelo de la reducción requiere de O(log2 N ) pasos
(tres pasos en este caso) para calcular el resultado. Con P hilos ejecutándose fı́sicamente en
paralelo (P procesadores), la complejidad en tiempo es O(N/P + log2 N ).
84
La reducción de paso logarı́tmico mostrada en la Figura 3.15 muestra una estrategia de
intercalado entre elementos. Esta estrategia mejora el uso del ancho de banda cuando se lee de
memoria global de la GPU. Una versión de la reducción que utiliza pares es intuitiva, pero el
tener un solo hilo accediendo a regiones adyacentes de memoria causa transacciones de memoria
separadas. El factor de intercalado en la Figura 3.15 es cuatro. En memoria global de la GPU,
el utilizar un factor de intercalado múltiplo del producto del tamaño de bloque y el tamaño de
malla (blockDim.x*gridDim.x) produce un buen desempeño por que todas las transacciones de
memoria se producen de manera adyacente.
Primitiva:
reducción(Re , ⊕, Rs )
Entrada:
Re [1, ..., n]: Vector a agregar (atributo de medida),
⊕: Operador binario
Rs : Valor agregado
Rs = ⊕Re [i], i, ..., n
Salida:
Función:
Tabla 3.8: Prototipo de la primitiva reducción. La función de esta primitiva es evaluar los elementos de un arreglo de
entrada Re mediante un operador binario asociativo ⊕, produciendo un solo elemento Rs como salida.
La implementación de la reducción en GPU fue realizada a través de un método de dos fases,
empleando el nivel de memoria compartida para almacenar resultados parciales por bloque de
hilos. La Figura 3.16 muestra una representación conceptual de como se realiza el proceso de
reducción en la GPU. Como hemos mencionado, en la GPU los hilos se agrupan en bloques
cuando se lanza una función kernel, la ilustración de la Figura 3.16 muestra un vector de 16
elementos siendo reducido por cuatro bloques de hilos en una primera fase. Los resultados intermedios de las reducciones efectuadas por cada bloque de hilos son almacenados en la memoria
compartida de la GPU, en el pequeño ejemplo de la figura los dos bloques estarı́an integrados
por dos hilos. Al termino de la segunda fase, los resultados de las reducciones efectuadas por los
bloques de hilos son situados en un vector almacenado en memoria global. La segunda fase de
la reducción toma el vector producido por la primera y usando un solo bloque de hilos obtiene
el resultado final de la reducción.
85
Figura 3.16: Reducción de un vector en dos fases. En la primera fase, un conjunto de bloques de hilos reduce varias
secciones de un arreglo o vector de valores, produciendo resultados parciales. La segunda fase obtiene el resultado
final de la reducción usando un solo bloque de hilos sobre los resultados parciales de la primera fase.
Este enfoque de dos fases de utilizó para lidiar con la imposibilidad de sincronizar bloques de
hilos en CUDA. Es decir, se necesita lanzar una segunda función kernel a falta de un mecanismo
de comunicación entre bloques que permita determinar cuando procesar el resultado final. Cabe
mencionar que existen otras formas de implementar la reducción en GPUs. Para este trabajo,
este enfoque fue adecuado.
La reducciónSegmentada opera de manera similar a la reducción, con la diferencia de que
en esta primitiva se tiene un arreglo particionado a través de un vector de claves, siendo su
función reducir cada partición a un escalar. En [17] se realizó la reducción sobre segmentos
arbitrarios del vector de entrada utilizando un vector de claves y [52] presentó por primera vez
la reducciónSegmentada en un bloque de hilos de GPU. Cada hilo de GPU reduce los elementos
de un arreglo con el mismo valor de clave, en una sumatoria por ejemplo, cada hilo está encargado
de acumular los valores de algunos de los elementos de un arreglo, tales elementos deben contar
con el mismo valor de clave para ser acumulados. Si un hilo alcanza una posición del vector a
reducir con un valor de clave diferente entonces tal hilo ya ha terminado de reducir la sección
del segmento que le corresponde.
86
Primitiva:
reducciónSegmentada(Re , claves, ⊕, Rs )
Entrada:
Re [1, ..., n]: Vector a agregar (atributo de medida),
claves[1, ..., m]: Particiones en Re , ⊕: Operador binario
Rs [1, .., m]: Vector de valores agregados
Rs [i] = ⊕Re [j], donde claves[j] = i, i = 1, ..., m
Salida:
Función:
Tabla 3.9: Prototipo de la primitiva reducciónSegmentada. La función de esta primitiva es evaluar varios segmentos
de un arreglo de entrada Re mediante un operador binario asociativo ⊕, produciendo un elemento como salida por
cada segmento, es decir, un arreglo de salida Rs . Los segmentos se encuentran delimitados por valores continuos en
un segundo arreglo (claves) del mismo tamaño del arreglo a evaluar.
La Figura 3.17 muestra la una conceptualización de la reducciónSegmentada sobre un vector
de 16 elementos dividido en tres segmentos. Como puede observarse, las claves que delimitan los
segmentos a reducir comparten valores y cada segmento se reduce a un escalar. Este modelo se
implementó en este trabajo ya que facilita la producción de agregados para un cierto cuboide.
Figura 3.17: ReducciónSegmentada de un vector. Los elementos del vector de la ilustración están divididos en tres
segmentos y la operación realizada es una sumatoria. La reducción de cada segmento del vector produce a un escalar.
En la ilustración de la Figura 3.17 los valores de clave para los tres segmentos son diferentes
(0, 1 y 2), pero basta con un cambio en el valor de clave para delimitar un nuevo segmento.
87
3.2.7.
Construcción de cuboides
En los métodos de generación de cubos de datos SPCube y GPUgenCube que se presentarán
en la siguiente sección, los cuboides son organizados en tareas, es decir, grupos de cuboides que
comparten una cierta caracterı́stica. De manera que los 2n cuboides o vistas del cubo de datos
quedan repartidas entre las tareas. Este agrupamiento se realiza con el fin de calcular grupos
de cuboides en paralelo. Al calcular una de estas tareas, se designa un hilo de CPU para cada
cuboide en ella, que producirá y escribirá en memoria secundaria las tuplas correspondientes.
Como se mencionó, en el caso de que la instrucción de cubo de datos a ejecutar incluya alguna
función como SUM, MAX, MIN o AVG, esta será resuelta mediante la GPU produciendo un
vectores de valores agregados que le serán proporcionado a los hilos a fin de construir las tuplas de
los cuboides correspondientes. Recordando el proceso de partición y los marcadores producidos
en esta fase, el hilo encargado de producir tuplas para un cierto cuboide usa los marcadores
para indexar el vector que contiene los datos de la relación base y ası́ recuperar los valores que
le corresponden a cada atributo del cuboide.
Como ejemplo supongamos que se tiene una relación base de tres dimensiones (A, B y C)
con 1000 tuplas y las primitivas de partición determinaron los marcadores 0, 100, 200 y 500 para
la construcción del cuboide (ABC,COUNT(*)), es decir, se tienen cuatro particiones (0-99, 100199, 200-499, 500 - 999). El hilo encargado del cuboide ABC puede entonces indexar el vector
que contiene los datos de la relación base en las posiciones 0, 100, 200 y 500 relativas a la sección
que contiene los valores correspondientes a cada atributo. Como la función de agregación en
este caso es COUNT(*), los marcadores proporcionan la información necesaria para producir
los valores agregados mediante operaciones aritméticas. Es decir, dado que la fase de partición
ha determinado los marcadores 0, 100, 200 y 500 entonces, el conteo de valores para la primera
partición es 100 (100-0), para la segunda 100 valores (200-100), para la tercera 300 (500-200) y
la para la cuarta 500 (1000-500), 1000 en total.
En lo que respecta a las funciones de agregación SUM, MAX, MIN que son efectuadas por la
GPU y la función algebraica AVG que se calcula en función de SUM y COUNT, los marcadores
producidos en la fase de partición son utilizados para producir las claves que serán empleadas
por la reducciónSegmentada. Considerando una vez más el ejemplo de los marcadores 0, 100, 200
y 500, las claves para la reducciónSegmentada se generan considerando el número de elementos
en cada partición, en este ejemplo para delimitar el primer segmento se habrá que generar una
secuencia de con 100 repeticiones del número cero, para el segundo 100 repeticiones del número
uno, para el tercero 300 repeticiones del número dos y para el cuarto segmento 500 repeticiones
del número tres. En conjunto las claves se almacenan en un arreglo para ser utilizadas por la
reducciónSegmentada de la siguiente manera: (0, ..., 0, 1, ..., 1, 2, ..., 2, 3, ..., 3), como se observa,
las claves de un mismo segmento comparten el mismo valor.
88
Dado que los datos se encuentran almacenados en un arreglo unidimensional, para efectuar la
reducción y reducciónSegmentada, solo se transfiere a la memoria de video la sección del vector
correspondiente al atributo a ser agregado y la información correspondiente a las particiones
(claves). Esto permite reducir los requerimientos de espacio en memoria de video, ya que al
construir un grupo de tuplas la parte que no requiere de cálculos se realiza mediante la CPU.
La Figura 3.18 muestra una conceptualización del proceso de construcción del cuboide (ABC,
SUM(D)) para una relación con cinco tuplas, como puede observarse, la columna “D” de la
relación base que va a ser agreda por la función SUM se transfiere a la GPU junto con un
arreglo de claves.
Figura 3.18: Representación del proceso de construcción de un cuboide de tres dimensiones utilizando la función SUM.
89
3.3.
Métodos paralelos de generación de cubos de datos
Esta sección presenta tres métodos para generación de cubos de datos. El proceso de construcción del cubo de datos se agiliza mediante el aprovechamiento de la tecnologı́a de procesadores multinúcleo y de muchos núcleos. Es decir, la GPU está a cargo de ejecutar las operaciones
que requieren aplicar gran cantidad de operaciones aritméticas y de comparación, como la aplicación de funciones de agregación y los ordenamientos, empleando un paralelismo de grano fino
a través de sus numerosos procesadores. En una fase posterior y a través de hilos de CPU, se
construyen tuplas completas para un grupo de cuboides usando los valores agregados generados
por la GPU.
Tipos de cubos de datos a resolver
En este trabajo se evaluó la construcción de cubos de datos completos y del tipo iceberg sobre
conjuntos de datos numéricos. Se evaluaron condiciones para cubos iceberg efectuando funciones
como: SUM, MAX, MIN, COUNT(*) y AVG. Para la aplicación de la poda Apriori en el método
MCBUC se requiere que las condiciones de cubos iceberg sean del tipo antimonotónicas [30] (i.e.,
condiciones tales como COUNT(*) ≥ 50). Tales condiciones tienen la propiedad de que si una
condición iceberg es violada para alguna celda c, entonces todo ancestro de c también violara
tal condición. Por ejemplo, si la cantidad de un articulo I vendido en un región R1 es menor
que 50, entonces el mismo artı́culo I vendido en una subregión de R1 nunca podrá satisfacer la
condición COUNT(*) ≥ 50.
90
3.3.1.
Método MCBUC
MCBUC es un método paralelo y recursivo basado en [3], la idea del método es mejorar
el desempeño de algoritmo BUC a través de la división de la totalidad de cuboides entre los
procesadores de un CPU multinúcleo. El algoritmo BUC calcula los cuboides o vistas del cubo
iniciando con los menos detallados, recorriendo conceptualmente la estructura lattice en profundidad y amplitud. El BUC consta de un paralelismo inherente, ya que su recursividad forma
árboles de procesamiento independientes, cada uno con raı́z en un cierto atributo o dimensión
del cubo de datos. La Figura 3.19 muestra un árbol de procesamiento construido por el algoritmo BUC para un cubo de cuatro dimensiones, como puede observarse, el método calcula el
cuboide menos detallado en la jerarquı́a de lattice y posteriormente la recursividad comienza en
los cuboides de una dimensión. En el método MCBUC, cada uno de los árboles con raı́z en un
atributo o dimensión se vuelve una tarea como lo muestra la Tabla 3.10. En otra palabras para
un cubo con n atributos habrá n tareas.
Figura 3.19: Árbol de procesamiento del algoritmo BUC [3] para un cubo de datos de cuatro dimensiones. Los números
indican el orden de cálculo para los cuboides.
Tareas
Combinaciones
T1
ALL
T2
A, AB, ABC, ABCD, ABD, AC, ACD, AD
T3
B, BC, BCD, BD
T4
C, CD
T5
D
Tabla 3.10: Asignación de tareas en el método MCBUC para un cubo de datos de cuatro dimensiones.
91
La principal ventaja que proporciona el hecho de que MCBUC y BUC generen al cubo
de datos de comenzando por los cuboides de menos detalle en la lattice es permitir la poda
Apriori, que consiste en podar todas aquellas particiones que no cumplen con el umbral de
soporte mı́nimo en cubos de tipo iceberg. Esta estrategia reduce la cantidad de cálculos, se
explicará posteriormente con más detalle. El algoritmo BUC [3] tiene la desventaja de que gran
parte del tiempo se desperdicia realizando operaciones de ordenamiento, es susceptible a datos
con mediana dimensionalidad y baja cardinalidad, ya que conforme aumenta la dimensionalidad
en los datos hay mas vistas a generar y la baja cardinalidad generalmente conlleva a que los
grupos a agregarse sean grandes, impidiendo que el método pueda utilizar la poda, para mejorar
el desempeño en estas situaciones, MCBUC integra un paralelismo a nivel de tarea.
A diferencia de los otros métodos presentados en esta tesis, MCBUC no utiliza almacenamiento en memoria lineal, el ordenamiento se realiza mediante el algoritmo counting sort [42],
y la partición se realiza secuencialmente de manera similar a la de [40]. Más especı́ficamente,
cuando de ejecuta una cierta tarea, la recursividad del BUC ordena y particiona a los datos
en bruto, respecto a un solo atributo, entonces se toma a la primera partición de los datos
para ser agregada. Posteriormente, la recursividad procede ordenando y particionando sobre la
misma sección de los datos pero cada vez con más detalle, produciendo tuplas para los cuboides
involucrados. Una vez terminado el calculo de las tuplas correspondientes a una partición, se
procede a evaluar la siguiente partición de los datos en bruto, como se observa en la Figura 3.20.
La recursividad sobre una partición termina al no satisfacerse el umbral de soporte mı́nimo del
cubo iceberg.
Figura 3.20: Particionamiento usado en los métodos BUC [3] y MCBUC de un conjunto de datos de cuatro dimensiones.
Los ai son valores del atributo A, los valores bi corresponden al atributo B y ası́ sucesivamente.
92
Poda Apriori
La propiedad Apriori en el contexto de cubos de datos dice lo siguiente: “Si una celda dada
no satisface el soporte mı́nimo, entonces ninguno de sus ancestros (datos a mayor detalle) lo
hará”, esta propiedad se utilizará para reducir el cálculo en los cubos iceberg y fue propuesta en
el algoritmo Apriori para minerı́a de reglas de asociación [2]. En otras palabras si una condición
es violada por alguna celda c entonces todo ancestro de c también la violará. Las medidas que
obedecen este principio son conocidas como antimonotónicas. Véase la Figura 3.21
Figura 3.21: Ejemplo de poda Apriori. Los grupos que no cumplen con la cláusula iceberg son ignorados al construir
un cuboide. Para este ejemplo, se ignoran los grupos con menos de dos tuplas.
Algoritmo
El Algoritmo 4 muestra el pseudocódigo del método recursivo BUC [3]. Como puede observarse, el algoritmo genera el cuboide o vista de menos detalle en la jerarquı́a de lattice y
posteriormente se invoca recursivamente a BottomUpCube() iniciando en cada dimensión del
cubo de datos.
93
Algoritmo 4 BottomUpCube
Entrada:
entrada: Relación a ser agregada
dim: Dimensión inicial de la iteración
global constante numDims: El número total de dimensiones
global constante cardinalidad[numDims]: La cardinalidad de cada dimensión
global constante minsup: Número mı́nimo de tuplas en una partición para ser procesada
global salida: El registro de salida actual
global conteoDatos[numDims]: Almacena el tamaño de cada partición conteoDatos[i] es
una lista de enteros de tamaño cardinalidad[i]
Salida:
Un registro que es la agregación de la entrada
Recursivamente se invoca BottomUpCube(dim, ..., numDims) sobre entrada (cumpliendo
con minsup)
1: Agregar(entrada)
. Situar el resultado en salida
2: Si entrada.conteo() == 1 Entonces
. Optimización
3:
EscribirAncestros(entrada[0], dim)
4:
Regresar
5: Fin Si
6: Escribir salida
7: Para d ← dim; d < numDims; d + + hacer
8:
Sea C ← cardinalidad[d]
9:
Particionar(entrada, d, C, conteoDatos[d])
10:
Sea k ← 0
11:
Para i ← 0; i < C; i + + hacer
. Para cada partición
12:
Sea c ← conteoDatos[d][i]
13:
Si c ≥ minsup Entonces
. BottomUpCube se detiene aquı́
14:
salida.dim[d] ← entrada[k].dim[d]
15:
BottomUpCube(entrada[k, ..., k + c], d + 1)
16:
Fin Si
17:
k ←k+c
18:
Fin Para
19:
salida.dim[d] ← ALL
20: Fin Para
94
El Algoritmo 5 muestra un bosquejo del método MCBUC, simplemente un hilo de CPU se
encarga de iniciar la recursividad para un cierto atributo del cubo de datos.
Algoritmo 5 MCBUC
Entrada:
Re : Relación con n atributos o dimensiones (Ai , ..., An ), n es también el número de dimensiones del cubo
global minsup: Umbral de soporte mı́nimo
Salida:
Cuboides o vistas del cubo de datos
1: Agregar la vista o cuboide menos detallada del cubo
2: Agrupación de vistas o cuboides en tareas: similar al algoritmo BUC, i.e., subarboles de
procesamiento con raı́z en Ai .
3: Asignación de hilos: a cada sub árbol con raı́z en Ai (tareai ) se le asigna el hilo i.
4: Hacer en paralelo
5: Para cada sub árbol con raı́z en la dimensión Ai asignado al hilo hacer
6:
BottomUpCube(Re , Ai )
. La salida se escribe en un buffer local
7: Fin Para
8: Fin Hacer
95
Ejemplo
Para ejemplificar el flujo general de ejecución del método MCBUC, considere la siguiente
instrucción en SQL de un cubo iceberg:
SELECT A, B, COUNT(*) FROM R CUBE BY A, B HAVING COUNT(*) >= 1
El método MCBUC inicia por el cálculo del cuboide de menor detalle en la jerarquı́a de
lattice, generando una sola tupla. Como se ha mencionado, por cada dimensión del cubo de
datos hay una tarea donde el método MCBUC se ejecuta recursivamente a través del procesador
que le fue asignado para producir las tuplas correspondientes. En cada fase de la recursividad,
el método MCBUC particiona y agrega un grupo de tuplas hasta terminar con los cuboides
correspondientes a la tarea. La Figura 3.22 muestra un esquema del flujo de ejecución del
método MCBUC para la instrucción anterior sobre una relación con diez tuplas.
Figura 3.22: Esquema conceptual de la ejecución del método MCBUC para la generación de un cubo de datos de dos
dimensiones. Las flechas muestran el flujo de procesamiento recursivo de este método.
96
3.3.2.
Método SPCube
SPCube, es un método descendente ya que calcula los cuboides de mayor a menor detalle en
la jerarquı́a de lattice. La idea principal es paralelizar el ordenamiento, partición, agregación y
construcción de cuboides integrando la estrategia del calculo de cuboides a partir de ancestros
en la jerarquı́a de lattice. La Figura 3.23 conceptualiza la idea general de este método ası́ como
el uso de las primitivas.
Figura 3.23: Esquema del método SPCube.
La asignación de cuboides en tareas dentro de este método se realiza respecto a los niveles de
la jerarquı́a de lattice, es decir, los cuboides son agrupados dependiendo del número de atributos
que se tomarán en cuenta para calcular los agregados. Por tanto, a lo más pueden procesarse
simultáneamente el número de cuboides de un cierto nivel en la jerarquı́a de lattice contenga.
97
Esta asignación de tareas permite calcular los cuboides en un cierto nivel de la lattice a partir
de sus ancestros en la jerarquı́a. Por ejemplo, para un cubo de datos de cuatro dimensiones se
tendrı́an la asignación de tareas como lo muestran la Tabla 3.11 y la Figura 3.24, permitiendo
al método calcular los cuboides en una tarea a partir de los previamente calculados.
Figura 3.24: Lattice para un cubo de datos de cuatro dimensiones. Las flechas indican rutas potenciales de cálculo
para el método SPCube.
Tareas
Combinaciones
T1
ABCD
T2
ABC, ABD, ACD, BCD
T3
AB, AC, AD, BC, BD, CD
T4
A, B, C, D
T5
ALL
Tabla 3.11: Asignación de tareas en el método SPCube para un cubo de datos de cuatro dimensiones. Los cuboides
son agrupados de acuerdo al número de atributos.
98
Con el fin de reducir cálculo, SPCube selecciona ciertas vistas del cubo de datos para generar
a partir de ella otras más que se encuentran en niveles de menor detalle en la jerarquı́a de lattice.
De la Figura 3.24 se puede observar que algunos vistas o cuboides podrı́an ser calculados a partir
de varios ancestros, tal es el caso del cuboide B de la tarea 3 que comparte atributos con los
cuboides más detallados AB, BC y BD, la selección de un ancestro adecuado implica la necesidad
de conocer el costo de cada una de estas vistas. El costo de una vista o cuboide se refiere a
su número de tuplas, en este método este número se estima a través de la cardinalidad de
sus atributos, es decir, el número de tuplas en una vista está en función del producto de las
cardinalidades de los atributos tomados en cuenta para generarla. Este número también puede
estimarse mediante alguna técnica estadı́stica como [18, 44].
Además del costo, para determinar el ancestro más adecuado es necesario revisar si el cuboide
a calcularse comparte ordenamiento con el ancestro. Considerando de nuevo a B, este cuboide
podrı́a ser calculado directamente a partir de BC o BD sin necesidad de ordenar los datos, sin
embargo si se calcula a partir de AB será necesario re-ordenar los datos para calcularlo. La
Figura 3.25 muestra un ejemplo de árbol de procesamiento generado por el método SPCube a
partir de la estimación de costos en la jerarquı́a de lattice. Los guiones en los arcos indican los
casos en que es necesario ordenar el cuboide ancestro.
Figura 3.25: Árbol de procesamiento generado por el método SPCube. Los números a la derecha de cada combinación
de atributos indican la cantidad de tuplas del cuboide, M y K indican millones y miles respectivamente. Estas cantidades
permiten realizar una selección que producirá el menor costo al generar vistas del cubo a partir de otras más detalladas
(ancestros en la jerarquı́a de lattice).
99
Algoritmo
La selección del cuboide ancestro que produce el menor costo al calcular otro menos detallado
en la jerarquı́a de lattice se realiza a través del método que se muestra en el Algoritmo 6. El
algoritmo obtiene el costo de toda vista que comparte atributos con una vista v y devuelve la
referencia de la que produjo el menor costo.
Algoritmo 6 padreMenorCosto
Entrada:
global ndimensiones: Número de dimensiones en los datos
cardinalidad[ndimensiones]: Cardinalidades de los atributos de la relación base
vistas: Conjunto de las combinaciones de atributos correspondientes a cada vista del cubo
v: Combinación de atributos de la vista a ser evaluada
Salida:
padreMı́nimo: Combinación de atributos del ancestro de costo mı́nimo para la vista v
1: padreMı́nimo ← cuboideBase
. combinación de la vista más detallada en vistas
2: costoMı́nimo ← costo(padreMı́nimo)
3: Para cada vistai en vistas hacer
4:
Si v ⊂ vistai y costoMı́nimo > costo(vistai ) Entonces
5:
costoMı́nimo ← costo(vistai )
6:
padreMı́nimo ← vistai
7:
Fin Si
8: Fin Para
9: Regresar padreMı́nimo
10: Función costo(vista)
. Costo de calcular una vista a partir de esta
11:
costo ← 1
12:
Para cada Ai en vista hacer
13:
costo ← cardinalidad[i] ∗ costo
14:
Fin Para
15:
Regresar costo
16: Fin Función
100
El Algoritmo 7 muestra el flujo general del método SPCube ası́ como el uso de las primitivas
paralelas. Como puede observarse, SPCube calcula grupos de vistas del cubo de datos usando
la referencia del ancestro de menor costo proporcionada por el Algoritmo 6. Si el orden de
los atributos de un cuboide ancestro lo permite, el ordenamiento se omite. En caso de que la
instrucción de cubo de datos requiera la aplicación de una función como SUM, MAX, MIN o
AVG esta se realiza mediante las primitivas paralelas de reducción.
Algoritmo 7 SPCube
Entrada:
Re : Vector de tuplas
global minsup: Umbral de soporte mı́nimo
Salida:
Cuboides o vistas del cubo de datos
1: Agrupar vistas en tareas y calcular la vista o cuboide base
2: Estimar de cardinalidad para cada atributo de los datos: cardinalidad[ndimensiones]
3: Para cada tareai en tareas hacer
4:
Para cada vistaj en tareai hacer
5:
padreMı́nimo ← padreMenorCosto(vistaj , cardinalidad)
6:
Si vistaj y padreMı́nimo no comparten orden Entonces
7:
ordenar tuplas de padreMı́nimo
8:
Fin Si
9:
Efectuar particiónLocal() para vistaj
10:
Efectuar particiónCuboide() empleando el resultado de particiónLocal()
11:
Efectuar reducción() o reducciónSegmentada() sobre el atributo de medida de
padreMı́nimo
. Agregar (GPU)
12:
Fin Para
13:
Hacer en paralelo
. Generación de cuboides en paralelo (CPU multinúcleo)
14:
Para cada vistaj en tareai hacer
15:
Emplear el resultado de reducción() y reducciónSegmentada() para construir las tuplas de vistaj que cumplen minsup, indexando Re de acuerdo con los marcadores generados
por particiónCuboide().
16:
Escribir tuplas de la vistaj en memoria secundaria
17:
Fin Para
18:
Fin Hacer
19: Fin Para
101
Ejemplo
Para ejemplificar el flujo general de ejecución del método SPCube, considere la siguiente
instrucción para generar un cubo de datos de tres dimensiones:
SELECT A, B, C, SUM(D) FROM R CUBE BY A, B, C
El método SPCube inicia por realizar un ordenamiento a los datos en bruto para calcular el
cuboide más detallado de cubo de datos, en este ejemplo, es el cuboide ABC. A partir de este
momento se inicia la fase de construcción del plan de construcción de cubo de datos, donde se
estima el costo los cuboides restantes y se determina a partir de que ancestro se generará cada
cuboide restante. Se toma en cuenta también si es posible evitar el ordenamiento del ancestro al
generar un nuevo cuboide. La Figura 3.26 muestra el flujo de ejecución del algoritmo SPCube
para la instrucción anterior. Como la función de agregación del cubo es SUM, las primitivas de
reducción se encargarı́an de producir los valores agregados correspondientes a cada cuboide.
Figura 3.26: Esquema de ejecución del método SPCube para un cubo de tres dimensiones. Los cuboides son generados
a partir de otros más detallados en lugar de los datos en bruto.
102
3.3.3.
Método GPUgenCube
En está sección se presenta el método GPUgenCube para cálculo de cubos de datos completos
y de tipo iceberg, el cual está diseñado con la idea de paralelizar el proceso de construcción del
cubo de datos ejecutando varios cuboides simultáneamente usando hilos de CPU y delegando el
cálculo de agregados como SUM, MAX, MIN y AVG a una GPU. La idea general del método
ası́ como el uso de primitivas se esquematiza en la Figura 3.27.
Figura 3.27: Esquema del método GPUgenCube.
Este método inicia con una fase de generación de tareas, donde cada uno de los 2n cuboides
son asignados a una determinada tarea. Posteriormente, si se requiere de ejecutar una función
como SUM, MAX, MIN o AVG, las primitivas de reducción entran en funcionamiento para
producir valores agregados. Las tuplas para los cuboides en las tareas son armadas simultáneamente usando hilos de CPU y los valores previamente producidos (en el caso de SUM, MAX,
MIN y AVG).
103
El algoritmo de generación de tareas utilizado en GPUgenCube organiza los cuboides de
acuerdo a sus atributos con el fin de reducir operaciones de ordenamiento, por ejemplo, considere la generación de un cubo de datos con las dimensiones A, B, C y D, si se ordenan los
datos en bruto respecto a la combinación de atributos ABCD, entonces es posible calcular los
cuboides ABCD, ABC, AB, A y ALL en paralelo y sin ordenamientos adicionales. Ordenar los
datos en bruto respecto a la combinación de atributos del cuboide de más detalle en una tarea
permite también calcular las tuplas correspondientes a todos los cuboides de dicha tarea en una
sola pasada al flujo de entrada. Sin embargo, en primera instancia, este agrupamiento produce
algunas tareas que podrı́an ser re-agrupadas, es decir, tareas donde los cuboides comparten los
mismos atributos pero están organizados inversamente comenzando de izquierda a derecha, como los cuboides ABD, BD y D de la Figura 3.28. Un número elevado de tareas implica realizar
más escaneos a memoria secundaria para calcular la totalidad de los cuboides. Para reducir el
impacto de está situación, las tareas de un elemento son reagrupadas a través de un cambio en
el sentido de las combinaciones de sus cuboides como se explica a continuación:
1. Seleccionar las tareas de 1 elemento.
2. Seleccionar el cuboide c de más detalle en las tareas de 1 elemento.
3. Invertir el la combinación de atributos de c.
4. Buscar agrupar c con otros cuboides invirtiendo sus combinaciones de atributos en caso
de ser necesario.
5. Repetir procedimiento con los cuboides restantes
Considerando de nuevo los cuboides ABD, BD y D de la Figura 3.28, ordenar los datos se
respecto a la combinación ABD o DBA produce el mismo resultado, entonces, es posible calcular
ABD, BD y D en una sola pasada a los datos en bruto. La Figura 3.28 muestra una lattice para
un cubo de cuatro dimensiones, seguido de ella se encuentra la Tabla 3.12 que recopila las listas
de cuboides agrupados como se mencionó. Estas listas serán ejecutadas una a una, generando
en paralelo las tuplas de los cuboides en ellas.
104
Figura 3.28: Lattice para un cubo de datos de cuatro dimensiones.
Tareas
Combinaciones
T1
ABCD, ABC, AB, A, ALL
T2
BCD, BC, B
T3
ACD, AC
T4
CD, C
T5
DBA, DB, D
T6
AD
Tabla 3.12: Asignación de tareas en el método GPUgenCube para un cubo de datos de cuatro dimensiones. Las tareas
son procesadas una a una, empleando paralelismo de grano fino a través de las primitivas paralelas y generando tuplas
en paralelo para los cuboides de una cierta tarea en paralelo.
Nótese de la Tabla 3.12 que los cuboides BCD, BC y B por ejemplo, no podrı́an ser agrupados
junto con los cuboides de T1 , ya que aunque se tienen atributos en común, el ordenamiento
respecto a la combinación ABCD producirı́a incorrectamente más particiones para BCD, BC y
B por incluir adicionalmente al atributo A.
Algoritmo
A continuación se presentan los detalles generales del método GPUgenCube. Este método a
diferencia de SPCube no reduce cálculos usando ancestros en la jerarquı́a de lattice, en su lugar
la agrupación de los cuboides en tareas permite reducir las operaciones de ordenamiento.
105
En caso de que la instrucción de cubo de datos requiera la aplicación de una función como
SUM, MAX, MIN o AVG esta se realiza mediante las primitivas paralelas de reducción. Para el
caso de la función COUNT, no se requieren operaciones de reducción, en su lugar, los conteos son
realizados al vuelo cuando se construyen las tuplas de un cuboide usando operaciones aritméticas
y la información de particionamiento.
Algoritmo 8 GPUgenCube
Entrada:
Re : Vector de tuplas
global minsup: Umbral de soporte mı́nimo
Salida:
Cuboides o vistas del cubo de datos
1: Agrupar cuboides en tareas
2: Para cada (tareai en tareas) hacer
3:
Ordenar tuplas respecto a la vista de más detalle en tareai
4:
Efectuar particiónLocal() respecto a la vista de más detalle en tareai
5:
Para cada (vistaj en tareai ) hacer
6:
Efectuar particiónCuboide() empleando el resultado de particiónLocal()
7:
Si se incluyen funciones como SUM, MAX MIN o AVG Entonces . Agregar (GPU)
8:
Efectuar reducción() o reducciónSegmentada() sobre el atributo de medida de Re
9:
Fin Si
10:
Fin Para
11:
Hacer en paralelo
. Generación de cuboides en paralelo (CPU multinúcleo)
12:
Para cada vistaj en tareai hacer
13:
Si se incluyen funciones como SUM, MAX MIN o AVG Entonces
14:
Emplear el resultado de reducción() y reducciónSegmentada() para construir las
tuplas de vistaj que cumplen minsup, indexando Re de acuerdo con los marcadores generados por particiónCuboide().
15:
Escribir tuplas de la vistaj en memoria secundaria
16:
Si no
17:
Construir las tuplas de vistaj que cumplen minsup, indexando Re de acuerdo con
los marcadores generados por particiónCuboide().
18:
Escribir tuplas de la vistaj en memoria secundaria
19:
Fin Si
20:
Fin Para
21:
Fin Hacer
22: Fin Para
106
Ejemplo
Para ejemplificar el flujo general de ejecución del método GPUgenCube, considere la siguiente
instrucción SQL para construir un cubo de 3 dimensiones:
SELECT A, B, C, SUM(D) FROM R CUBE BY A, B, C
El método GPUgenCube inicia por agrupar los cuboides en tareas de acuerdo a la combinación sus atributos como se explicó anteriormente. Las tareas se ejecutan una a una, ordenando
los datos solo una vez por cada tarea (respecto al cuboide de más detalle en una cierta tarea).
Los cuboides de cada tarea son construidos en paralelo utilizando hilos de CPU y en el caso de
que se requiera resolver funciones como SUM, MAX, MIN o AVG, las primitivas de reducción
se encargan de calcular los valores agregados correspondientes. La Figura 3.29 muestra el flujo
general del método GPUgenCube en la construcción de un cubo de datos de tres dimensiones
usando la función de agregación SUM. Al ejecutar una tarea, los datos en bruto son ordenados,
particionados, agregados y posteriormente un conjunto de hilos se encarga de construir y escribir
a disco las tuplas de los cuboides en la tarea.
Figura 3.29: Esquema de ejecución del método GPUgenCube para un cubo de tres dimensiones. Las tareas son
ejecutadas una a una por GPUgenCube. Los cuboides de una cierta tarea son generados y escritos simultáneamente
a memoria secundaria por un hilo de CPU.
107
3.3.4.
Comparativa de los métodos para cubos de datos
La Tabla 3.13 muestra una comparativa entré las caracterı́sticas con las que cuentan los
métodos presentados. En la columna “Uso de memoria”, el indicador Bajo se refiere a que el
método solo requiere realizar un ordenamiento a la relación base para calcular los cuboides
de una cierta tarea, Medio indica que el método requiere re-ordenar varias fuentes de datos
(cuboides) para calcular una tarea, Alto indica que el método necesita replicar y re-ordenar
la relación base para ejecutar el cálculo de las tareas. Las funciones implementadas para los
métodos se presentan en la Tabla 3.14.
Método
Uso de memoria
Estrategias para
cubos completos
Poda en
cubos iceberg
Usa primitivas
paralelas
MCBUC
Alto
No
Si
No
SPCube
Medio
Si
No
Si
Bajo
Si
No
Si
GPUgenCube
Tabla 3.13: Resumen de caracterı́sticas para los métodos SPCube, MCBUC y GPUgenCube.
En cuanto a funcionalidad, las implementaciones de los métodos incluyen las funciones de
agregación que muestran una marca de verificación en la Tabla 3.14.
Método
SUM MAX
MIN
MCBUC
COUNT AVG
X
SPCube
X
X
X
GPUgenCube
X
X
X
X
X
Tabla 3.14: Funciones de agregación en los métodos MCBUC, SPCube y GPUgenCube. Las marcas de verificación
indican las funciones implementadas para cada método.
Ası́ mismo, los métodos SPCube y GPUgenCube constan de 2 modos de escritura, CSV
y SQLite. El formato CSV (en inglés, Comma-Separated Values) permite guardar las vistas
del cubo de datos en formato tabular usando un archivo de texto en el que las columnas se
separan mediante una coma, esta opción escribe cada vista del cubo de datos en un archivo con
este formato. La opción SQLite produce un archivo con las vistas del cubo de datos para ser
consultado usando el lenguaje SQL a través del motor de base de datos conocido como SQLite
[45].
108
Capı́tulo 4
Pruebas y resultados
En esta sección se presenta una evaluación de los métodos propuestos para verificar su
eficiencia y escalabilidad. El software desarrollado en este trabajo fue comparado contra otros
algoritmos de cubos de datos bien conocidos en la literatura de bases de datos como BUC [3] y
MM-Cubing [43], cuyas implementaciones se encuentran disponibles en el paquete Illimine [46]
de la universidad de Illinois en Urbana-Champaign. A continuación se describe el escenario donde
se realizó la experimentación. En primera instancia se da una descripción de la configuración
de hardware del sistema de pruebas y posteriormente se dan las caracterı́sticas de los conjuntos
de datos utilizados.
109
4.1.
Equipo de pruebas
Todos los métodos fueron programados en los lenguajes C/C++ y CUDA C sobre un sistema
AMD Athlon II X3. La Tabla 4.1 presenta una especificación detallada de las caracterı́sticas del
sistema donde se realizaron las pruebas.
Software
Sistema operativo
Compilador de C
Versión de CUDA
Linux v3.0.0
GCC v4.4.3
5.5
CPU
Modelo
Número de núcleos
Tamaño de caché L1 y L2
Memoria RAM
AMD Athlon II X3 455
3 a 3.3GHZ
128KB, 1.5MB
4GB DDR3 (1333MHZ)
GPU
Modelo
Número de multiprocesadores
Total de núcleos
Tamaño de memoria compartida
caché L1 y caché L2
Memoria de video
NVIDIA GTX 460
7
336 a 1.35GHZ
48KB,
16KB, 512KB
1GB (1800MHZ)
Tabla 4.1: Especificación técnica del sistema de pruebas.
4.2.
Conjunto de datos de prueba
Las pruebas fueran realizadas utilizando datos de tipo entero para generar los cubos de
datos. Los conjuntos de datos fueron producidos de manera sintética a través del generador de
datos incluido en el paquete Illimine [46]. Este generador permite variar nivel del sesgo en los
datos respecto al factor de Zipf, especificar de manera individual la cardinalidad de los atributos
o dimensiones de los datos (número de valores diferentes por columna) ası́ como ajustar el rango
en el cual se generarán los valores de dichos atributos.
110
4.3.
Desempeño del ordenamiento
Se probó la implementación del algoritmo de ordenamiento radix sort paralelo en una GPU
contra otros algoritmos de ordenamiento por clave para CPU. Las implementaciones de los
algoritmos para CPU fueron obtenidas de varias bibliotecas para C++ disponibles en la web,
como son: la biblioteca GNU estándar de C/C++ [15], Boost [6] y Thrust [22]. Las Figuras 4.1
y 4.2 muestran el desempeño de los algoritmos sobre conjuntos de datos de hasta 120 millones
de elementos.
GPU radix sort
Thrust (CPU)
GNU Std C++ library
Boost
9
8
7
Tiempo (S)
6
5
4
3
2
1
0
10
20
30
40
50
60
70
80
90
100
110
120
Millones de enteros
Figura 4.1: Desempeño de algoritmos de ordenamiento sobre conjuntos de datos distribuidos aleatoriamente.
GPU radix sort
Thrust (CPU)
GNU Std C++ library
Boost
9
8
7
Tiempo (S)
6
5
4
3
2
1
0
10
20
30
40
50
60
70
80
90
100
110
120
Millones de enteros
Figura 4.2: Desempeño de algoritmos de ordenamiento sobre conjuntos de enteros en orden decreciente.
111
La Figura 4.2 muestra la ejecución de los algoritmos de ordenamiento incremental sobre
varios conjuntos de enteros de 32 bits en un rango de 1 a 1000 previamente ordenados de manera decreciente. En la Figura 4.1 puede observarse el tiempo promedio de varios grupos de
ejecuciones sobre conjuntos de 1 a 120 millones de enteros de 32 bits generados aleatoriamente en un rango de 0 a 1000. En todos los casos el ordenamiento incremental más rápido se
realizó utilizando la GPU.
4.4.
Desempeño de los algoritmos de cubos de datos
Las pruebas de esta sección consistieron en medir el tiempo de ejecución de los métodos
propuestos en esta tesis en la generación de cubos de datos completos y de tipo iceberg. Se
utilizaron conjuntos de hasta 10 millones de tuplas variando parámetros como número de tuplas,
dimensiones (5 - 10 dimensiones), cardinalidad (20 - 100 valores diferentes por dimensión) y
distribución (0 - 5 respecto al factor de Zipf). Los conjuntos de datos utilizados son sintéticos
y constan de la misma cardinalidad en todos sus atributos, los valores para un atributo son
enteros en un rango de 1 a la cardinalidad del atributo. Las gráficas incluyen el tiempo de
acceso a memoria secundaria.
En las gráficas de esta de esta sección, dimensiones denota el número de dimensiones para
los cubos de datos (se producen 2d vistas para un cubo con d dimensiones), cardinalidad indica
el número de valores por cada cada dimensión o atributo de los datos, tuplas se refiere a la
cantidad de registros de los datos en bruto (m=millones) utilizados para la generación de los
cubos, minsup es el umbral de soporte mı́nimo que toda tupla de una vista del cubo iceberg
debe cumplir y por ultimo, sesgo el nivel de sesgo o valor de Zipf de los datos. Cuando el sesgo
es cero, los datos están distribuidos de manera uniforme; conforme el sesgo aumenta los datos
se encuentran más sesgados. El valor de sesgo se aplica a todas las dimensiones. Las gráficas se
encuentran clasificadas de acuerdo a las siguientes cuatro categorı́as:
Cubo completo: Gráficas sobre la generación de todas las vistas del cubo de datos. Se
compara al método GPUgenCube presentado en esta tesis contra los métodos BUC [3] y
MM-Cubing [43] utilizando la función COUNT(*).
Cubo iceberg: Gráficas sobre la generación de las vistas o la parte de las vistas del cubo de
datos que cumplen con un umbral de soporte mı́nimo (minsup) previamente especificado.
Se compara al método GPUgenCube presentado en esta tesis contra los métodos BUC
[3] y MM-Cubing [43] utilizando la función COUNT(*) y la clausula iceberg COUNT(*)
≥ minsup.
112
Sesgo: Gráficas donde el desempeño del proceso de generación del cubo se mide respecto a
variaciones en el nivel de sesgo de los datos utilizando la función COUNT(*) y la clausula
iceberg COUNT(*) ≥ minsup. Se compara al método GPUgenCube presentado en esta
tesis contra los métodos BUC [3] y MM-Cubing [43].
Funciones de agregación: Gráficas donde se muestra el desempeño de los métodos SPCube
y GPUgenCube presentados en esta tesis en la generación de cubos iceberg utilizando las
funciones de agregación SUM, MAX, MIN, COUNT y AVG. Las gráficas de funciones
de agregación se muestran por separado a falta de implementaciones completas de los
métodos BUC [3] y MM-Cubing [43] (solo realizan COUNT(*)).
4.4.1.
Cubo completo
El siguiente grupo de gráficas muestra el desempeño de GPUgenCube, BUC [3] y MMCubing [43], en la generación de cubos de datos completos (se generan todas las vistas del cubo
de datos). Se utiliza COUNT(*) como función de agregación. El eje horizontal de las gráficas
indica el nivel de variación de un cierto parámetro al generar los cubos de datos.
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
2
4
6
8
10
Tuplas (Millones)
Figura 4.3: Generación del cubo completo, dimensiones = 7, cardinalidad = 10, sesgo = 0.
113
GPUgenCube
BUC
MMCubing
1000
900
800
Tiempo (S)
700
600
500
400
300
200
100
0
10
20
30
40
50
Cardinalidad
Figura 4.4: Generación del cubo completo, tuplas = 10m, dimensiones = 7, sesgo = 0.
GPUgenCube
BUC
MMCubing
9000
8000
7000
Tiempo (S)
6000
5000
4000
3000
2000
1000
0
5
6
7
8
9
10
Dimensiones
Figura 4.5: Generación del cubo completo, tuplas = 10m, cardinalidad = 20, sesgo = 0.
114
GPUgenCube
BUC
MMCubing
500
450
400
Tiempo (S)
350
300
250
200
150
100
50
0
2
4
6
8
10
Tuplas (Millones)
Figura 4.6: Generación del cubo completo, dimensiones = 8, cardinalidad = 10, sesgo = 0.
330
GPUgenCube
BUC
MMCubing
300
270
Tiempo (S)
240
210
180
150
120
90
60
30
0
10
20
30
40
50
Cardinalidad
Figura 4.7: Generación del cubo completo, tuplas = 10m, dimensiones = 7, sesgo = 0, sin considerar el tiempo de
escritura a disco.
115
2200
GPUgenCube
BUC
MMCubing
2000
1800
Tiempo (S)
1600
1400
1200
1000
800
600
400
200
0
5
6
7
8
9
10
Dimensiones
Figura 4.8: Generación del cubo completo, tuplas = 10m, cardinalidad = 20, sesgo = 0, sin considerar el tiempo
de escritura a disco.
4.4.2.
Cubo iceberg
El siguiente grupo de gráficas muestra el desempeño de los métodos GPUgenCube, BUC
[3] y MM-Cubing [43] en la generación de cubos de datos iceberg utilizando COUNT(*) como
función de agregación y COUNT(*) ≥ minsup como clausula iceberg. El eje horizontal de las
gráficas indica el nivel de variación de un cierto parámetro al generar los cubos de datos.
116
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
20
40
60
80
100
Cardinalidad
Tiempo (S)
Figura 4.9: Generación del cubo iceberg, tuplas = 10m, dimensiones = 7, sesgo = 0, minsup = 100.
300
280
260
240
220
200
180
160
140
120
100
80
60
40
20
0
GPUgenCube
BUC
MMCubing
20
40
60
80
100
Cardinalidad
Figura 4.10: Generación del cubo iceberg, tuplas = 10m, dimensiones = 8, sesgo = 0, minsup = 100.
117
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
50
100
150
200
250
Minsup
Figura 4.11: Generación del cubo iceberg, tuplas = 10m, cardinalidad = 20, dimensiones = 7, sesgo = 0.
GPUgenCube
BUC
MMCubing
400
360
320
Tiempo (S)
280
240
200
160
120
80
40
0
10
20
30
40
50
60
70
80
90
100
110
Minsup
Figura 4.12: Generación del cubo iceberg, tuplas = 10m, cardinalidad = 20, dimensiones = 8, sesgo = 0.
118
4.4.3.
Sesgo
El siguiente grupo de gráficas muestra el desempeño de los métodos GPUgenCube, BUC [3]
y MM-Cubing [43] en la generación de cubos de datos iceberg, utilizando conjuntos de datos
enteros distribuidos de manera distinta. Los datos fueron sesgados utilizando valores que van de
cero a cinco respecto a Zipf. Conforme el valor de Zipf aumenta, los datos están más sesgados. Se
utiliza COUNT(*) como función de agregación y COUNT(*) ≥ minsup como clausula iceberg.
El eje horizontal de las gráficas indica el nivel de variación de un cierto parámetro al generar
los cubos de datos.
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
0
1
2
3
4
5
Sesgo
Figura 4.13: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m, dimensiones =
7, cardinalidad = 20, minsup = 100.
119
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
0
1
2
3
4
5
Sesgo
Figura 4.14: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m, dimensiones =
7, cardinalidad = 40, minsup = 50.
GPUgenCube
BUC
MMCubing
200
180
160
Tiempo (S)
140
120
100
80
60
40
20
0
0
1
2
3
4
5
Sesgo
Figura 4.15: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m, dimensiones =
7, cardinalidad = 100, minsup = 50.
120
GPUgenCube
BUC
MMCubing
400
360
320
Tiempo (S)
280
240
200
160
120
80
40
0
0
1
2
3
4
5
Sesgo
Figura 4.16: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m, dimensiones =
8, cardinalidad = 100, minsup = 10.
4.4.4.
Funciones de agregación
El siguiente grupo de gráficas muestra el desempeño de los métodos SPCube y GPUgenCube
presentados en esta tesis en la generación de cubos de datos iceberg usando las funciones SUM,
MAX MIN y AVG. Cada gráfica de esta sección muestra la generación de un grupo de cubos
de datos donde se utiliza distinta función de agregación pero una misma clausula iceberg. El eje
horizontal de las gráficas indica el nivel de variación de un cierto parámetro al generar los cubos
de datos.
121
Tiempo (S)
SPCube resolviendo SUM, MAX, MIN y usando la clausula iceberg SUM() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
5
6
7
8
9
10
Dimensiones
Figura 4.17: SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup = 100.
SUM
MAX
MIN
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.18: SPCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 100.
122
Tiempo (S)
SPCube resolviendo SUM, MAX, MIN y usando la clausula iceberg MAX() ≥
minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
5
6
7
8
9
10
Dimensiones
Figura 4.19: SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup = 100.
SUM
MAX
MIN
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.20: SPCube: Generación del cubo iceberg variando en el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 20.
123
Tiempo (S)
SPCube resolviendo SUM, MAX, MIN y usando la clausula iceberg MIN() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
5
6
7
8
9
10
Dimensiones
Figura 4.21: SPCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup = 100.
SUM
MAX
MIN
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.22: SPCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 20.
124
Tiempo (S)
GPUgenCube resolviendo SUM, MAX, MIN, COUNT, AVG y usando la clausula
iceberg SUM() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
COUNT(*)
AVG
5
6
7
8
9
10
Dimensiones
Figura 4.23: GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup =
100.
SUM
MAX
MIN
COUNT(*)
AVG
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.24: GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 100.
125
Tiempo (S)
GPUgenCube resolviendo SUM, MAX, MIN, COUNT, AVG y usando la clausula
iceberg MAX() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
COUNT(*)
AVG
5
6
7
8
9
10
Dimensiones
Figura 4.25: GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup =
20.
SUM
MAX
MIN
COUNT(*)
AVG
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.26: GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 20.
126
Tiempo (S)
GPUgenCube resolviendo SUM, MAX, MIN, COUNT, AVG y usando clausula
iceberg MIN() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
COUNT(*)
AVG
5
6
7
8
9
10
Dimensiones
Figura 4.27: GPUgenCube: Generación del cubo iceberg, AVG, tuplas = 10m, sesgo = 1, cardinalidad = 20,
minsup = 20.
SUM
MAX
MIN
COUNT(*)
AVG
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.28: GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 20.
127
Tiempo (S)
GPUgenCube resolviendo SUM, MAX, MIN, COUNT, AVG y usando la clausula
iceberg COUNT(*) ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
COUNT(*)
AVG
5
6
7
8
9
10
Dimensiones
Figura 4.29: GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup =
100.
SUM
MAX
MIN
COUNT(*)
AVG
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.30: GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 100.
128
Tiempo (S)
GPUgenCube resolviendo SUM, MAX, MIN, COUNT, AVG y usando la clausula
iceberg AVG() ≥ minsup
1700
1600
1500
1400
1300
1200
1100
1000
900
800
700
600
500
400
300
200
100
0
SUM
MAX
MIN
COUNT(*)
AVG
5
6
7
8
9
10
Dimensiones
Figura 4.31: GPUgenCube: Generación del cubo iceberg, tuplas = 10m, sesgo = 1, cardinalidad = 20, minsup =
20.
SUM
MAX
MIN
COUNT(*)
AVG
150
Tiempo (S)
120
90
60
30
0
0
1
2
3
4
5
Sesgo
Figura 4.32: GPUgenCube: Generación del cubo iceberg variando el nivel de sesgo (Zipf) de los datos, tuplas = 10m,
dimensiones = 7, cardinalidad = 20, minsup = 20.
129
4.4.5.
Observaciones
Cubo completo
En el conjunto de gráficas del cubo completo (Figuras 4.3, 4.4, 4.5 y 4.6) muestra una
ligera diferencia entre los métodos GPUgenCube y MM-Cubing, como puede observarse en la
Figuras 4.3 y 4.6 donde los tiempos entre ejecuciones de GPUgenCube y MM-Cubing difieren
de manera casi constante. La Figura 4.6 muestra una diferencia máxima de 65 segundos entre
las ejecuciones de ambos métodos, mientras que en la Figura 4.3 la diferencia máxima es apenas
de 13 segundos. En primera instancia esto se debe a que el tiempo este proceso está dominado
por las operaciones de entrada/salida a memoria secundaria.
El algoritmo BUC obtiene un buen desempeño en ciertos casos solamente (Figuras 4.4 y
4.5), sobre todo en cubos de datos donde el tamaño del flujo de salida fue más elevado. Esto se
puede observar con claridad en la Figura 4.5, donde el tiempo del BUC para la generación del
cubo de datos de 10 dimensiones es ligeramente superior a 6000 segundos.
GPUgenCube obtuvo un desempeño regular en este conjunto de experimentos (Figuras 4.3,
4.4, 4.5 y 4.6), esto se debe en primera instancia a que GPUgenCube paraleliza la escritura, contrastando con BUC y MM-Cubing, donde las operaciones de entrada/salida a memoria
secundaria se realizan de manera serializada, lo cual favorece ligeramente al almacenamiento
mecánico utilizado en las pruebas. Para dar una idea del tamaño del flujo de salida producido
en este grupo de experimentos, la generación de un cubo de datos completo, a partir de 10 millones de tuplas, 10 dimensiones y una cardinalidad de 20 en cada atributo produjo un resultado
que ocupó 120GB de espacio en disco (4,504,968,176 tuplas).
Las Figuras 4.7 y 4.8 muestran el desempeño de GPUgenCube, BUC y MMCubing en la
generación del cubo completo sin considerar el tiempo de escritura a disco.
Cubo iceberg
De las Figuras 4.9, 4.10, 4.11 y 4.12 puede observase que GPUgenCube obtiene un desempeño
superior en todos los casos, superando al BUC a pesar su estrategia de poda Apriori y a MMCubing que reduce cálculos en base a frecuencia. En el problema del cubo iceberg la escritura
a disco se ve reducida, acentuando la aceleración proporcionada por la paralelización de los
cálculos en el método GPUgenCube.
130
Sesgo
Las gráficas de la sección “Sesgo” (Figuras 4.13, 4.14, 4.15 y 4.16) muestran un experimentos
realizados sobre conjuntos de datos distribuidos utilizando valores de 0 a 5 respecto al factor
de Zipf para controlar el sesgo de los datos. El método GPUgenCube se muestra estable sobre
esta variación como resultado del ordenamiento paralelo. Sin embargo, el sesgo beneficia a MMCubing, desempeñándose bastante bien en las ejecuciones donde los datos se encuentran más
sesgados. En el caso del BUC, aunque también se beneficia del sesgo, obtiene el peor desempeño
en todo el grupo de experimentos.
Funciones de agregación
La primitivas de reducción permiten a los métodos SPCube y GPUgenCube resolver funciones de agregación distributivas y algebraicas en un tiempo logarı́tmico. En la mayorı́a de los
casos, la función AVG tomó más tiempo, como puede observarse en las Figuras 4.17 - 4.22 y
4.23 - 4.32, esto se debe en parte a que implica el calculo de SUM y COUNT. Por otro lado,
considerando que los datos usados para generar el cubo de datos son enteros, la escritura a memoria secundaria producida por la función AVG es de tipo flotante y por tanto tiene un volumen
mayor en comparación con los valores enteros producidos como salida por las funciones SUM,
MAX, MIN y COUNT.
Acceso a disco
Cuando se lee un conjunto de datos de un archivo en disco, el procesador necesita esperar
que la lectura finalice para iniciar el procesamiento sobre ellos. De manera similar sucede para
cuando el procesador realiza una operación de escritura a disco, el procesador permanece en
estado de espera mientras se realiza la escritura. Este tiempo de acceso es un problema para
los métodos paralelos de esta tesis, como los discos duros son mecánicos, es necesario esperar
a que el disco rote al sector que se requiere, limitando el desempeño de los múltiples hilos
escritores. La latencia de acceso a disco es de alrededor de 13 milisegundos (varı́a en función de
la velocidad de rotación del disco) mientras que la latencia de acceso a la memoria RAM es de
aproximadamente 83 nanosegundos.
131
Capı́tulo 5
Conclusiones
En este trabajo se presentaron tres métodos paralelos para generación de cubos de datos:
MCBUC, SPCube y GPUgenCube, que aprovechan las ventajas de los CPUs multinúcleo y
recientes GPUs a través de un conjunto de primitivas paralelas.
En el capı́tulo 1 de la tesis se mencionó que se tenia por objetivo general el diseño e implementación de métodos paralelos para generación de cubos de datos completos y de tipo iceberg,
aprovechando las ventajas del procesamiento en GPUs y CPUs multinúcleo. El objetivo general
se cumplió con el diseño de los métodos antes mencionados.
Ası́ también, se dijo que como objetivos particulares de la tesis se tenia:
Diseñar e implementar un conjunto de primitivas paralelas que permitan el manejo eficiente de datos aprovechando el paralelismo de GPUs y CPUs multinúcleo, ası́ como los
niveles de memoria de la GPU.
Diseñar e implementar métodos paralelos de generación de cubos de datos completos y de
tipo iceberg empleando tecnologı́a multihilo y primitivas paralelas.
Diseñar e Implementar funciones de agregación distributivas y algebraicas para los métodos de cubos de datos.
El primer objetivo particular se cumplió con la implementación de las primitivas que aprovechan
el paralelismo de GPUs y CPUs multinúcleo ası́ como la memoria compartida de la GPU. El
segundo objetivo particular se cumplió con el uso de hilos POSIX y de las primitivas paralelas
del capı́tulo 3 para ejecutar subrutinas en los métodos de generación de cubos de datos. El
tercer objetivo particular se cumplió con la implementación de las funciones distributivas SUM,
MAX, MIN, COUNT y la función algebraica AVG.
132
Además, se pudo observar mediante las pruebas, que de los métodos presentados en este
trabajo, GPUgenCube obtuvo el mejor desempeño, superando también en la mayorı́a de los
casos a los algoritmos BUC [3] y MM-Cubing [43]. Cabe destacar que solo se probó a GPUgenCube contra MM-Cubing y BUC ejecutando la función COUNT esto a falta de funcionalidad
en las implementaciones de MM-Cubing y BUC; por lo que las gráficas de las secciones “Cubo
completo”, “Cubo iceberg” y “Sesgo” no muestran la ventaja del paralelismo en la ejecución
de funciones de agregación. GPUgenCube tiene un manejo de memoria económico, ya que la
división de tareas permite calcular un grupo de cuboides en una sola pasada a disco, además
de ser capaz de resolver funciones agregación distributivas y algebraicas de manera eficiente.
En lo que respecta al método SPCube, obtuvo un desempeño inferior al de GPUgenCube a
pesar de utilizar una estrategia para reducir cálculo, esto a causa de las lecturas y ordenamientos adicionales a las vistas del cubo. Ası́ también, como resultado de la implementación
paralela del ordenamiento y la reducción, los métodos GPUgenCube y SPCube demostraron
experimentalmente ser poco susceptibles al sesgo y cardinalidad.
Por otra parte, el formato para almacenamiento de tuplas en memoria lineal utilizado por
los métodos SPCube y GPUgenCube promueve la utilización del ancho de banda del bus PCI
Express al acceder y transferir secciones contiguas de memoria entre la RAM y la memoria global
de la GPU, reduciendo ası́ los tiempos de transferencia entre estas memorias. Ası́ mismo, este
formato en combinación con un ordenamiento externo permite fácilmente extender los métodos
para calcular el cubo de datos a partir de conjuntos de datos que no caben en memoria principal.
En cuanto a las limitaciones de este trabajo, a pesar del paralelismo con el que cuentan los
métodos presentados en esta tesis, la implementación está limitada por la tecnologı́a de almacenamiento utilizada durante las pruebas, es decir, la alta cantidad y paralelización de operaciones
de entrada/salida en discos duros normalmente conlleva a una degradación en el desempeño general del software, causada por la baja velocidad de acceso a disco y el movimiento continuo
de la cabeza lectora del disco. Por ello, la escritura a memoria secundaria es un factor crı́tico
en el desempeño de los métodos paralelos de cubos de datos, ya que de no ser implementada
de manera cuidadosa puede resultar en un desempeño pobre en situaciones donde las operaciones de entrada y salida a memoria secundaria predominan, como es el caso del cálculo del
cubo completo, donde la salida ocupa cientos de gigabytes de espacio en disco. Esta situación
conllevó a obtener un mejor desempeño general en la construcción de cubos iceberg, donde la
cantidad de información a escribir en memoria secundaria se reduce y los cálculos aumentan.
133
5.1.
Trabajo futuro
Entre los trabajos que pueden considerarse para extender el alcance de esta investigación se
encuentran:
Incluir un ordenamiento externo para extender la funcionalidad de los métodos paralelos
a conjuntos de datos que no caben en memoria.
La construcción del cubo de datos empleando jerarquı́as en las dimensiones, esto es, con
frecuencia se requiere analizar un conjunto de datos a diferentes niveles de detalle con
respecto a un cierto atributo (e.g., campos como la fecha o la región geográfica), lo que
implica la necesidad de manejar de jerarquı́as en los datos.
Incluir el operador CUBE BY en el dialecto SQL de algún manejador de base de datos
relacional como [45] para generación de cubos de datos empleando los métodos presentados
en este trabajo.
Agregar soporte a los métodos para actualización incremental de cubos de datos.
Extender el diseño e implementación los métodos paralelos presentados en este trabajo
a clusters de computadoras o GPUs conectados a través de un bus de alta velocidad
utilizando almacenamiento paralelo.
134
Referencias
[1] Sameet Agarwal, Rakesh Agrawal, Prasad M. Deshpande, Ashish Gupta, Jeffrey F. Naughton, Raghu Ramakrishnan, y Sunita Sarawagi. On the computation of multidimensional
aggregates. En IN PROCEEDINGS OF THE INTERNATIONAL CONFERENCE ON
VERY LARGE DATABASES, págs. 506–521. 1996.
[2] Rakesh Agrawal y Ramakrishnan Srikant. Fast algorithms for mining association rules in
large databases. En Proceedings of the 20th International Conference on Very Large Data
Bases, VLDB ’94, págs. 487–499. Morgan Kaufmann Publishers Inc., San Francisco, CA,
USA, 1994. ISBN 1-55860-153-8. URL http://dl.acm.org/citation.cfm?id=645920.
672836.
[3] Kevin Beyer y Raghu Ramakrishnan. Bottom-up computation of sparse and iceberg cube.
SIGMOD Rec., 28(2):359–370, 1999. ISSN 0163-5808. doi:10.1145/304181.304214. URL
http://doi.acm.org/10.1145/304181.304214.
[4] Guy E. Blelloch. Vector models for data-parallel computing. MIT Press, Cambridge, MA,
USA, 1990. ISBN 0-262-02313-X.
[5] Guy E. Blelloch, Charles E. Leiserson, Bruce M. Maggs, C. Greg Plaxton, Stephen J.
Smith, y Marco Zagha. A comparison of sorting algorithms for the connection machine
cm-2. En Proceedings of the third annual ACM symposium on Parallel algorithms and
architectures, SPAA ’91, págs. 3–16. ACM, New York, NY, USA, 1991. ISBN 0-89791-4384. doi:10.1145/113379.113380. URL http://doi.acm.org/10.1145/113379.113380.
[6] Boost. Boost c++ library. 2013. URL http://www.boost.org/.
[7] Alejandro Botello, Adolfo Guzmán Arenas, y Renato Barrera. Query resolution in independent databases by partial integration. En International Congress on Data Mining and
Information Systems, ICDIS 2007. Centro de Investigación en Computación - Instituto
Politécnico Nacional, 2007.
135
[8] Ying Chen, F. Dehne, T. Eavis, y A. Rau-Chaplin. Building large rolap data cubes in parallel. En Database Engineering and Applications Symposium, 2004. IDEAS ’04. Proceedings.
International, págs. 367–377. 2004. ISSN 1098-8068. doi:10.1109/IDEAS.2004.1319810.
[9] Edgar Chávez, Karina Figueroa, y Gonzalo Navarro. Proximity searching in high dimensional spaces with a proximity preserving order. En Alexander F. Gelbukh, Alvaro de Albornoz, y Hugo Terashima-Marı́n, eds., MICAI, tomo 3789 de Lecture Notes in Computer Science, págs. 405–414. Springer, 2005. ISBN 3-540-29896-7. URL
http://dblp.uni-trier.de/db/conf/micai/micai2005.html#ChavezFN05.
[10] Jeffrey Dean y Sanjay Ghemawat. Mapreduce: simplified data processing on large clusters.
Commun. ACM, 51(1):107–113, 2008. ISSN 0001-0782. doi:10.1145/1327452.1327492. URL
http://doi.acm.org/10.1145/1327452.1327492.
[11] Andrea C. Dusseau, David E. Culler, Klaus Erik Schauser, y Richard P. Martin. Fast
parallel sorting under logp: Experience with the cm-5. IEEE Transactions on Parallel and
Distributed Systems, 7:791–805, 1996.
[12] Message Passing Interface Forum. Mpi standard. URL http://www.mpi-forum.org/docs/
docs.html.
[13] The Apache Software Foundation. Apache hadoop. URL http://hadoop.apache.org/.
[14] Veronica Gil-Costa, Cesar Ochoa, y A. Marcela Printista. Suffix array performance analisis
for multi-core platforms. Computación y Sistemas, 17(3):391–399, 2013. ISSN 1405-5546.
[15] GNU. Standard c++ library. 2013. URL http://gcc.gnu.org/libstdc++/.
[16] Jim Gray, Surajit Chaudhuri, Adam Bosworth, Andrew Layman, Don Reichart, Murali
Venkatrao, Frank Pellow, y Hamid Pirahesh. Data cube: A relational aggregation operator
generalizing group-by, cross-tab, and sub-totals. Data Min. Knowl. Discov., 1(1):29–53,
1997. ISSN 1384-5810. doi:10.1023/A:1009726021843. URL http://dx.doi.org/10.1023/
A:1009726021843.
[17] William Gropp, Ewing Lusk, y Anthony Skjellum. Using MPI (2nd ed.): portable parallel
programming with the message-passing interface. MIT Press, Cambridge, MA, USA, 1999.
ISBN 0-262-57132-3.
[18] Peter J. Haas, Jeffrey F. Naughton, S. Seshadri, y Lynne Stokes. Sampling-based estimation of the number of distinct values of an attribute. En Proceedings of the 21th
International Conference on Very Large Data Bases, VLDB ’95, págs. 311–322. Morgan
136
Kaufmann Publishers Inc., San Francisco, CA, USA, 1995. ISBN 1-55860-379-4. URL
http://dl.acm.org/citation.cfm?id=645921.673295.
[19] Jiawei Han, Jian Pei, Guozhu Dong, y Ke Wang. Efficient computation of iceberg cubes
with complex measures. SIGMOD Rec., 30(2):1–12, 2001. ISSN 0163-5808. doi:10.1145/
376284.375664. URL http://doi.acm.org/10.1145/376284.375664.
[20] Bingsheng He, Naga K. Govindaraju, Qiong Luo, y Burton Smith. Efficient gather and
scatter operations on graphics processors. En Proceedings of the 2007 ACM/IEEE Conference on Supercomputing, SC ’07, págs. 46:1–46:12. ACM, New York, NY, USA, 2007.
ISBN 978-1-59593-764-3. doi:10.1145/1362622.1362684. URL http://doi.acm.org/10.
1145/1362622.1362684.
[21] J. L. Hennessy y D. A. Patterson. Computer Architecture: A quantitative approach. Morgan
Kauffman Publishers Inc., 2002.
[22] Jared Hoberock y Nathan Bell. Thrust: A parallel template library. 2012. URL http:
//thrust.github.io/.
[23] IEEE. Ieee std 1003.1. URL http://standards.ieee.org/findstds/standard/1003.
1-2008.html.
[24] Donald E. Knuth. The Art of Computer Programming, Volume III: Sorting and Searching.
Addison-Wesley, 1973. ISBN 0-201-03803-X.
[25] Mariela Lopresti, Natalia Miranda, Fabiana Piccoli, y Nora Reyes. Solving multiple queries
through the permutation index in gpu. Computación y Sistemas, 17(3):341–356, 2013. ISSN
1405-5546.
[26] Hua Luan, Xiao-Yong Du, y Shan Wang. Cache-conscious data cube computation on a
modern processor. Journal of Computer Science and Technology, 24:708–722, 2009. ISSN
1000-9000. URL http://dx.doi.org/10.1007/s11390-009-9253-0. 10.1007/s11390-0099253-0.
[27] Gilberto Martı́nez y Adolfo Guzmán. Búsqueda de patrones de comportamiento en cubos
de datos. En Segundo taller internacional de minerı́a de datos, MINDAT 2000, págs. 163–
179. Colegio de Postgraduados y Centro de Investigación en Computación, Texcoco, estado
de México., 2000.
[28] Microsoft. Directx developer center. 2012. URL http://msdn.microsoft.com/en-us/
directx/.
137
[29] Open MPI. A high performance message passing library. URL http://www.open-mpi.org.
[30] Raymond T. Ng, Laks V. S. Lakshmanan, Jiawei Han, y Alex Pang. Exploratory mining
and pruning optimizations of constrained associations rules. SIGMOD Rec., 27(2):13–24,
1998. ISSN 0163-5808. doi:10.1145/276305.276307. URL http://doi.acm.org/10.1145/
276305.276307.
[31] Raymond T. Ng, Alan Wagner, y Yu Yin. Iceberg-cube computation with pc clusters.
SIGMOD Rec., 30(2):25–36, 2001. ISSN 0163-5808. doi:10.1145/376284.375666. URL
http://doi.acm.org/10.1145/376284.375666.
[32] NVIDIA.
Fermi compute architecture whitepaper.
URL http://www.nvidia.
com/content/PDF/fermi_white_papers/NVIDIA_Fermi_Compute_Architecture_
Whitepaper.pdf.
[33] NVIDIA. What is cuda. 2012. URL http://developer.nvidia.com/what-cuda.
[34] NVIDIA. Cuda c programming guide. 2013. URL http://docs.nvidia.com/cuda/
cuda-c-programming-guide/.
[35] NVIDIA. Nvidia corporation. 2013. URL http://www.nvidia.com/page/home.html.
[36] OpenGL. About opengl. 2012. URL http://www.opengl.org/about/.
[37] Stefan Popov, Johannes Günther, Hans-Peter Seidel, y Philipp Slusallek. Stackless kd-tree
traversal for high performance GPU ray tracing. Computer Graphics Forum, 26(3):415–424,
2007. (Proceedings of Eurographics).
[38] Angel Omar Cervantez Ramı́rez. Paralelización de un subconjunto de consultas SQL con
unión natural utilizando una GPU. Tesis de maestrı́a, Centro de Investigación en Computación - Instituto Politécnico Nacional, 2012.
[39] Jun Rao y Kenneth A. Ross. Cache conscious indexing for decision-support in main memory. En Proceedings of the 25th International Conference on Very Large Data Bases,
VLDB ’99, págs. 78–89. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 1999.
ISBN 1-55860-615-7. URL http://dl.acm.org/citation.cfm?id=645925.671362.
[40] Kenneth A. Ross y Divesh Srivastava. Fast computation of sparse datacubes. En Proceedings of the 23rd International Conference on Very Large Data Bases, VLDB ’97, págs.
116–125. Morgan Kaufmann Publishers Inc., San Francisco, CA, USA, 1997. ISBN 1-55860470-7. URL http://dl.acm.org/citation.cfm?id=645923.670993.
138
[41] Nadathur Satish, Mark Harris, y Michael Garland. Designing efficient sorting algorithms
for manycore gpus. En Proceedings of the 2009 IEEE International Symposium on Parallel&Distributed Processing, IPDPS ’09, págs. 1–10. IEEE Computer Society, Washington, DC, USA, 2009. ISBN 978-1-4244-3751-1. doi:10.1109/IPDPS.2009.5161005. URL
http://dx.doi.org/10.1109/IPDPS.2009.5161005.
[42] Robert Sedgewick. Algorithms in C. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1990. ISBN 0-201-51425-7.
[43] Zheng Shao, Jiawei Han, y Dong Xin. Mm-cubing: Computing iceberg cubes by factorizing the lattice space. En Proceedings of the 16th International Conference on Scientific
and Statistical Database Management, SSDBM ’04, págs. 213–. IEEE Computer Society,
Washington, DC, USA, 2004. ISBN 0-7695-2146-0. doi:10.1109/SSDBM.2004.53. URL
http://dx.doi.org/10.1109/SSDBM.2004.53.
[44] Amit Shukla, Prasad M. Deshpande, Jeffrey F. Naughton, y Karthikeyan Ramasamy. Storage estimation for multidimensional aggregates in the presence of hierarchies. págs. 522–531.
1996.
[45] SQLite. About sqlite. URL http://www.sqlite.org/about.html.
[46] UIUC. Illimine. URL http://illimine.cs.uiuc.edu/.
[47] Baoyuan Wang y Yizhou Yu. Parallel h-tree based data cubing on graphics processors. Int.
J. Software and Informatics, 6(1):61–87, 2012.
[48] Hwu Wen-mei, Kurt-Keutzer, y Timothy G. Mattson.
The concurrency challenges.
IEEE Design & Test of Computers, 25(4):312 – 320, 2008.
ISSN
07407475. URL http://search.ebscohost.com/login.aspx?direct=true&db=iih&AN=
33558699&lang=es&site=ehost-live.
[49] Dong Xin, Jiawei Han, Xiaolei Li, y Benjamin W. Wah. Star-cubing: computing iceberg
cubes by top-down and bottom-up integration. En Proceedings of the 29th international
conference on Very large data bases - Volume 29, VLDB ’03, págs. 476–487. VLDB Endowment, 2003. ISBN 0-12-722442-4. URL http://dl.acm.org/citation.cfm?id=1315451.
1315493.
[50] Marco Zagha y Guy E. Blelloch. Radix sort for vector multiprocessors. En In Proceedings
Supercomputing ’91, págs. 712–721. 1991.
139
[51] Guoliang Zhou y Hong Chen. Parallel cube computation on modern cpus and gpus.
The Journal of Supercomputing, 61:394–417, 2012. ISSN 0920-8542. doi:10.1007/
s11227-011-0575-7. URL http://dx.doi.org/10.1007/s11227-011-0575-7.
[52] Kun Zhou, Qiming Hou, Rui Wang, y Baining Guo. Real-time kd-tree construction on
graphics hardware. ACM Trans. Graph., 27(5):126:1–126:11, 2008. ISSN 0730-0301. doi:
10.1145/1409060.1409079. URL http://doi.acm.org/10.1145/1409060.1409079.
140

Documentos relacionados