Optimización de la multiplicación matriz-vector para muchas matrices pequeñas

8

Estoy buscando acelerar los productos de vector de matriz, pero todo lo que leo es sobre cómo hacerlo para matrices muy grandes. Mi caso, las matrices son pequeñas, pero la cantidad de veces que se debe hacer es muy grande.

¿Qué métodos, si hay alguno, existen para optimizar esto? ¿Sería más rápido construir una matriz de bloques diagonales realmente grande a partir de las matrices pequeñas y un vector grande hecho de los vectores más pequeños y utilizar las técnicas para las aceleraciones de vectores de matriz grandes? ¿O la configuración de la matriz global y el vector mataría algún beneficio allí?

tpg2114
fuente
¿Tiene que multiplicar la misma matriz por muchos vectores o no hay reutilización de las matrices?
Brian Borchers
Dudo que usar las técnicas para matrices grandes para una gran diagonal de bloque le dará una aceleración significativa. ¿Qué tan pequeño es 'pequeño' en su caso y de cuántas matrices estamos hablando típicamente? ¿Sabe algo más acerca de estas matrices, por ejemplo, ¿describen rotaciones, etc.?
Christian Waluga
@BrianBorchers No hay reutilización de la matriz, es diferente para cada punto en cada paso
tpg2114
@ChristianWaluga Son 5x5 hasta 10x10 veces a veces, densas, no simétricas y no son diagonalmente dominantes en general. La cantidad de veces que debe hacerse varía según el caso, pero generalmente de 10000 a 60000 veces por paso de tiempo
tpg2114

Respuestas:

7

Antes de intentar optimizar su código, vale la pena preguntar si hay algo para optimizar para comenzar. Las bibliotecas que optimizan los productos de matriz de vectores lo hacen al solucionar dos problemas: limitaciones en el tamaño de la memoria caché y la latencia para cargar datos desde la memoria. El primero se realiza mediante el uso de los datos que están actualmente en la memoria caché en toda su extensión para todo lo que necesita ser utilizado antes de reemplazarlo por otros datos, el segundo se realiza mediante la captación previa de datos en la memoria caché antes de usarla realmente.

En su caso, tiene una intensidad aritmética relativamente pequeña de sus datos: carga los datos de la memoria, los usa exactamente una vez y luego pasa a la siguiente matriz. Esto deja solo la segunda vía para optimizar: buscar datos antes de usarlos.

Pero, como dije, antes de intentar optimizar las cosas, puede valer la pena averiguar lo que ya tiene: cronometra cuántos productos de matriz de vectores está haciendo por segundo, calcule cuántos bytes requiere cargar esto desde la memoria en su procesador, y luego compare esto con el ancho de banda del procesador que tiene en su máquina. Puede descubrir que no hay nada que pueda hacer para acelerar las cosas.

Wolfgang Bangerth
fuente
4

Se puede realmente no importa ya que sus matrices son caché ya contenía, pero debe estar llamando dgemv()o sgemv(), o el equivalente de la mejor biblioteca BLAS se puede obtener en sus manos. Debería probar el Intel MKL si puede acceder a él, y también BLIS o ATLAS o una de las muchas bibliotecas BLAS optimizadas que existen.

Bill Barth
fuente
1
Curiosamente, las rutinas BLAS se ejecutan más lentamente que la función intrínseca MATMUL en Fortran, incluso con implementaciones específicas de hardware y MKL.
tpg2114
1
No estoy completamente sorprendido por eso, pero tendría que ver el código para saberlo con certeza. Hay varias cuestiones por las que preocuparse. Tendría que sugerir que verifique la alineación de sus matrices antes de descartar el MKL, pero en estos tamaños pequeños, el MKL MATVEC puede no estar muy optimizado.
Bill Barth
Bill, un compañero de trabajo mío, se encontró con el mismo problema. La conclusión fue que, o bien había una sobrecarga no despreciable en la llamada MKL, o bien no estaba bien optimizada para matrices pequeñas. De cualquier manera, un matmul escrito a mano fue considerablemente más rápido al hacer una gran cantidad de multiplicaciones de matriz de 5x5.
Aurelius
2
O(norte2)O(norte3)
3
Si las matrices son muy pequeñas (por ejemplo, 4x4), intente probar una de las bibliotecas con plantilla; puede eliminar una gran cantidad de sobrecarga de llamadas de función. Eigen es un buen candidato.
Damien
4

Generar código C ++ y usar Eigen / Armadillo es una posibilidad, pero esto depende de la aplicación.

norte<8

Cuide la alineación de la memoria de sus datos (hasta 64 bytes alineados) y restrinja los punteros para facilitar la vida del compilador. No es necesario usar soporte multinúcleo con estos tamaños de matriz, la sobrecarga es mayor que la ganancia.

Usamos secuencias de comandos para generar automáticamente funciones separadas para cada combinación posible y almacenar en caché los punteros de función para llamadas consecutivas.

norte8

Pablo
fuente