Otimizando a multiplicação de vetores matriciais para muitas matrizes pequenas

8

Estou pensando em acelerar os produtos vetoriais de matriz, mas tudo o que leio é sobre como fazer isso para matrizes muito grandes. No meu caso, as matrizes são pequenas, mas o número de vezes que isso deve ser feito é muito grande.

Quais métodos, se houver, existem para otimizar isso? Seria mais rápido construir uma matriz de blocos diagonal realmente grande a partir das matrizes pequenas e um vetor grande composto por vetores menores e usar as técnicas para as grandes acelerações de vetores matriciais? Ou a criação da matriz global e do vetor mataria algum benefício lá?

tpg2114
fonte
Você precisa multiplicar a mesma matriz por muitos vetores ou não há reutilização das matrizes?
Brian Borchers
Duvido que o uso das técnicas de matrizes grandes para uma grande diagonal de blocos proporcione uma aceleração significativa. Quão pequeno é 'pequeno' no seu caso e quantas matrizes estamos falando tipicamente? Você sabe mais alguma coisa sobre essas matrizes, por exemplo, elas descrevem rotações etc.?
Christian Waluga
@BrianBorchers Não há re-utilizando a matriz, que é diferente para cada ponto a cada passo de tempo
tpg2114
@ChristianWaluga Eles são 5x5 até 10x10 às vezes, densos, não simétricos e nem diagonalmente dominantes em geral. Quantas vezes ele precisa ser varia feito sobre o caso, mas normalmente 10000 a 60000 vezes por passo de tempo
tpg2114

Respostas:

7

Antes de tentar otimizar seu código, vale a pena perguntar se há algo para otimizar, para começar. As bibliotecas que otimizam os produtos vetoriais matriciais o solucionam dois problemas: limitações no tamanho do cache e na latência para carregar dados da memória. O primeiro é feito usando os dados que estão atualmente no cache em sua extensão máxima para tudo o que ele precisa ser usado antes de substituí-lo por outros dados; o último é feito pré-buscando dados no cache antes de realmente usá-lo.

No seu caso, você tem relativamente pouca intensidade aritmética de seus dados - você carrega dados da memória, usa-os exatamente uma vez e depois passa para a próxima matriz. Isso deixa apenas o segundo caminho para otimizar: pré-buscar dados antes de usá-los.

Mas, como eu disse, antes de tentar otimizar as coisas, pode valer a pena descobrir o que você já tem: tempo quantos produtos vetoriais de matriz você está fazendo por segundo, calcule quantos bytes isso requer para carregar da memória no seu processador, e compare isso com a largura de banda do processador que você possui na sua máquina. Você pode achar que não há nada que possa fazer para tornar as coisas mais rápidas.

Wolfgang Bangerth
fonte
4

Ele não pode realmente importa desde suas matrizes são cache já contido, mas você deve estar chamando dgemv()ou sgemv()o equivalente da melhor biblioteca BLAS você pode chegar em suas mãos. Você deve experimentar o Intel MKL se puder obter acesso a ele, e também o BLIS ou ATLAS ou uma das muitas outras bibliotecas otimizadas do BLAS disponíveis no mercado.

Bill Barth
fonte
1
Curiosamente, as rotinas do BLAS são mais lentas que a função intrínseca do MATMUL no Fortran, mesmo com implementações específicas de hardware e MKL.
precisa saber é o seguinte
1
Não estou totalmente surpreso com isso, mas teria que ver o código para ter certeza. Há uma série de questões com que se preocupar. Eu teria que sugerir a verificação do alinhamento de suas matrizes antes de gravar o MKL, mas nesses tamanhos pequenos, o MKL MATVEC pode não ser altamente otimizado.
Bill Barth
Bill, um colega de trabalho meu, encontrou o mesmo problema. A conclusão foi que ou havia alguma sobrecarga não negligenciável na chamada MKL ou, caso contrário, não foi bem otimizado para matrizes pequenas. De qualquer maneira, um matmul escrito à mão era consideravelmente mais rápido ao fazer um número muito grande de multiplicações de matriz 5x5.
Aurelius
2
O matmul para um NxN multiplicando um Nx1 deve ter operações . Como você chegou a ? O(N2)O(N3)
Bill Barth
3
Se as matrizes forem muito pequenas (por exemplo, 4x4), tente dar uma chance a uma das bibliotecas de modelos - isso pode remover muitas sobrecargas de chamada de função. Eigen é um bom candidato.
Damien
4

Gerar código C ++ e usar Eigen / Armadillo é uma possibilidade, mas isso depende do aplicativo.

A solução para nós foi apenas escrever o resultado explicitamente para . Sem os loops, o código é muito rápido com compiladores modernos e suporte a vetores (sse2, avx2 e avx512 em 64 bits).N<8

Cuide do alinhamento da memória dos seus dados (até 64 bytes alinhados) e restrinja os ponteiros para facilitar a vida do compilador. Não é necessário usar o suporte multicore com esses tamanhos de matriz, a sobrecarga é maior que o ganho.

Usamos scripts para gerar automaticamente funções separadas para cada combinação possível e armazenar em cache os ponteiros de função para chamadas consecutivas.

Existe uma biblioteca barata e agradável para operações de matriz pequena que é altamente otimizada manualmente, chamada OptiVec , e funciona muito bem no nosso caso. Nós o usamos para .N8

Paulo
fonte