Resumen
La Multiplicación de matrices (o en inglés General Matrix Multiplication (GEMM)) es
un componente fundamental en la computación científica y en los marcos actuales dentro
del campo del Deep Learning (DL). Las implementaciones actuales de la GEMM están
escritas en su mayoría en C, aunque su poder computacional reside sobre un pequeño
y muy optimizado núcleo computacional, o micro-kernel, que suele estar codificado en
instrucciones vectoriales o ensamblador. Las bibliotecas de álgebra lineal del estado del
arte suelen incluir un único micro-kernel por arquitectura, normalmente implementado
manualmente por un experto.
Por un lado, el auge en la aplicación de Redes Neuronales Profundas (RNP) en una
gran variedad de campos científicos ha propiciado su uso, no solo en servidores de
cómputo intensivo, sino también en dispositivos de bajo consumo. Muchos de los cálculos
que se realizan en las RNPs, tanto en entrenamiento como en inferencia, se descomponen
en núcleos de álgebra lineal y se extraen de bibliotecas especializadas como Intel
Math Kernel Library (MKL) o BLAS-like Library Instantiation Software Framework
(BLIS). Además, los modelos de RNP traen asociados nuevos tamaños de problemas.
Estos modelos tienen capas convolucionales que se transforman en multiplicaciones de
matrices. Estas matrices difieren de las habituales en sus dimensiones. En los modelos
del estado del arte, las dimensiones de las matrices que se utilizan son de tamaños muy
rectangulares y alargados, llegando en casos muy extremos a casi 200 veces de diferencia
entre una dimensión y otra.
Los núcleos computacionales implementados por bibliotecas de álgebra lineal del estado
del arte, están diseñados para obtener un gran rendimiento con matrices muy grandes
y cuadradas. Sin embargo, en los modelos actuales de las RNPs, al ser las matrices
muy rectangulares, los núcleos ya existentes no son los adecuados para estos problemas
y obtienen un rendimiento inferior al que se podría obtener con el núcleo computacional
del tamaño adecuado. En los casos más extremos, es posible que el tamaño óptimo del
núcleo computacional varíe para cada una de las capas de los nuevos modelos de RNP.
Por otro lado, la gran variedad de hardware de bajo consumo hace prácticamente
imposible contar con núcleos computacionales optimizados para cada modelo. De esta
forma, surge la idea de implementar estos núcleos computacionales mediante la autogeneración
de código, con el objetivo de disponer rápidamente de núcleos computacionales
adaptados para cada arquitectura.
En esta tesis se estudia la forma de autogenerar estos núcleos computacionales utilizando
diversas técnicas para determinar su rendimiento. Más en detalle, en esta tesis
se han explorado las formas de autogeneración de núcleos computacionales utilizando
scripts de Python, generando tanto código en C con instrucciones vectoriales como código
ensamblador, templates de C++ y Apache Tensor Virtual Machine (TVM).
La implementación de un único núcleo computacional es una tarea compleja y propensa
a errores que requiere un alto conocimiento de la arquitectura donde se va a ejecutar
el programa. Elegir el tamaño óptimo del núcleo computacional para cada modelo de
RNP, y dentro de cada modelo, elegir el tamaño adecuado para cada capa, requiere tener
disponible una amplia variedad de núcleos computacionales. Mientras que implementar
manualmente cada uno de los núcleos computacionales posibles para cada arquitectura
es una tarea muy compleja, disponer de un generador automático permite probar todos
los núcleos y determinar el tamaño óptimo para cada caso de uso. De esta forma, es posible
superar el rendimiento obtenido por las bibliotecas de álgebra lineal actuales, que
solo implementan un único núcleo computacional por arquitectura, independientemente
del tamaño del problema.
La solución aquí propuesta se compara contra varias bibliotecas de álgebra lineal del
estado del arte, para diferentes casos de uso y en diversas arquitecturas.