Eu gostaria de escrever um programa que faz uso extensivo das funcionalidades da álgebra linear BLAS e LAPACK. Como o desempenho é um problema, fiz alguns benchmarking e gostaria de saber se a abordagem que usei é legítima.
Tenho, por assim dizer, três concorrentes e quero testar seu desempenho com uma simples multiplicação matriz-matriz. Os competidores são:
- Numpy, utilizando apenas a funcionalidade do
dot
. - Python, chamando as funcionalidades do BLAS através de um objeto compartilhado.
- C ++, chamando as funcionalidades do BLAS através de um objeto compartilhado.
Cenário
Implementei uma multiplicação matriz-matriz para diferentes dimensões i
. i
vai de 5 a 500 com um incremento de 5 e as matrizes m1
e m2
são configuradas assim:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Numpy
O código usado é parecido com este:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, chamando BLAS por meio de um objeto compartilhado
Com a função
_blaslib = ctypes.cdll.LoadLibrary("libblas.so")
def Mul(m1, m2, i, r):
no_trans = c_char("n")
n = c_int(i)
one = c_float(1.0)
zero = c_float(0.0)
_blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref(n), byref(n),
byref(one), m1.ctypes.data_as(ctypes.c_void_p), byref(n),
m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(zero),
r.ctypes.data_as(ctypes.c_void_p), byref(n))
o código de teste é semelhante a este:
r = numpy.zeros((i,i), numpy.float32)
tBlas = timeit.Timer("Mul(m1, m2, i, r)", "import numpy; from __main__ import i, m1, m2, r, Mul")
rBlas.append((i, tBlas.repeat(20, 1)))
3. c ++, chamando BLAS por meio de um objeto compartilhado
Agora o código c ++ naturalmente é um pouco mais longo, então reduzo as informações ao mínimo.
Eu carrego a função com
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Eu medi o tempo gettimeofday
assim:
gettimeofday(&start, NULL);
f(&no_trans, &no_trans, &dim, &dim, &dim, &one, A, &dim, B, &dim, &zero, Return, &dim);
gettimeofday(&end, NULL);
dTimes[j] = CalcTime(start, end);
onde j
está um loop rodando 20 vezes. Eu calculo o tempo que passou com
double CalcTime(timeval start, timeval end)
{
double factor = 1000000;
return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor;
}
Resultados
O resultado é mostrado no gráfico abaixo:
Questões
- Você acha que minha abordagem é justa ou há alguma sobrecarga desnecessária que posso evitar?
- Você esperaria que o resultado mostrasse uma discrepância tão grande entre a abordagem c ++ e python? Ambos estão usando objetos compartilhados para seus cálculos.
- Como prefiro usar python para meu programa, o que posso fazer para aumentar o desempenho ao chamar rotinas BLAS ou LAPACK?
Baixar
O benchmark completo pode ser baixado aqui . (JF Sebastian tornou esse link possível ^^)
r
matriz é injusta. Estou resolvendo o "problema" agora mesmo e posto os novos resultados.np.ascontiguousarray()
(considere a ordem C vs. Fortran). 2. certifique-se de quenp.dot()
usa o mesmolibblas.so
.m1
em2
têm oascontiguousarray
sinalizador comoTrue
. E numpy usa o mesmo objeto compartilhado que C faz. Quanto à ordem da matriz: Atualmente, não estou interessado no resultado do cálculo, portanto, a ordem é irrelevante.Respostas:
Eu executei seu benchmark . Não há diferença entre C ++ e numpy na minha máquina:
Parece justo porque não há diferença nos resultados.
Não.
Certifique-se de que numpy usa uma versão otimizada das bibliotecas BLAS / LAPACK em seu sistema.
fonte
ATUALIZAÇÃO (30.07.2014):
Eu executo novamente o benchmark em nosso novo HPC. Tanto o hardware quanto a pilha de software mudaram da configuração na resposta original.
Coloquei os resultados em uma planilha do Google (contém também os resultados da resposta original).
Hardware
Nosso HPC tem dois nós diferentes, um com CPUs Intel Sandy Bridge e outro com as CPUs Ivy Bridge mais recentes:
Sandy (MKL, OpenBLAS, ATLAS):
Ivy (MKL, OpenBLAS, ATLAS):
Programas
A pilha de software é para ambos os nós o sam. Em vez de GotoBLAS2 , OpenBLAS é usado e também há um ATLAS BLAS multithread que é definido para 8 threads (codificado).
Referência de produto pontual
O código de referência é o mesmo que abaixo. No entanto, para as novas máquinas, também executei o benchmark para os tamanhos de matriz 5000 e 8000 .
A tabela abaixo inclui os resultados do benchmark da resposta original (renomeado: MKL -> Nehalem MKL, Netlib Blas -> Nehalem Netlib BLAS, etc)
Desempenho de thread único:
Desempenho multi thread (8 threads):
Threads vs tamanho da matriz (Ivy Bridge MKL) :
Suite Benchmark
Desempenho de thread único:
Desempenho multiencadeado (8 fios):
Conclusão
Os novos resultados do benchmark são semelhantes aos da resposta original. OpenBLAS e MKL funcionam no mesmo nível, com exceção do teste de autovalor . Os Eigenvalue executa o teste única razoavelmente bem no OpenBLAS em modo de rosca única . No modo multithread, o desempenho é pior.
O "gráfico de tamanho da matriz vs threads" também mostra que, embora o MKL, assim como o OpenBLAS, geralmente escalem bem com o número de núcleos / threads, isso depende do tamanho da matriz. Para matrizes pequenas, adicionar mais núcleos não melhora muito o desempenho.
Há também um aumento de desempenho de aproximadamente 30% de Sandy Bridge para Ivy Bridge, o que pode ser devido a uma freqüência maior (+ 0,8 Ghz) e / ou melhor arquitetura.
Resposta Original (04.10.2011):
Algum tempo atrás eu tive que otimizar alguns cálculos / algoritmos de álgebra linear que foram escritos em python usando numpy e BLAS, então eu comparei / testei diferentes configurações numpy / BLAS.
Eu testei especificamente:
Eu executei dois benchmarks diferentes:
Aqui estão meus resultados:
Maquinas
Linux (MKL, ATLAS, No-MKL, GotoBlas2):
Mac Book Pro (Accelerate Framework):
Servidor Mac (Accelerate Framework):
Referência de produto pontual
Código :
Resultados :
Suite Benchmark
Código :
para obter informações adicionais sobre o pacote de benchmarks, clique aqui .
Resultados :
Instalação
A instalação do MKL incluiu a instalação do Intel Compiler Suite completo, que é bastante simples. No entanto, por causa de alguns bugs / problemas, configurar e compilar numpy com suporte MKL foi um pouco incômodo.
GotoBlas2 é um pequeno pacote que pode ser facilmente compilado como uma biblioteca compartilhada. No entanto, por causa de um bug, você deve recriar a biblioteca compartilhada após compilá-la para usá-la com o numpy.
Além dessa construção para plataforma de múltiplos alvos, não funcionou por algum motivo. Então, eu tive que criar um arquivo .so para cada plataforma para a qual quero ter um arquivo libgoto2.so otimizado .
Se você instalar o numpy do repositório do Ubuntu, ele irá instalar e configurar automaticamente o numpy para usar o ATLAS . A instalação do ATLAS a partir da fonte pode demorar algum tempo e requer algumas etapas adicionais (fortran, etc).
Se você instalar o numpy em uma máquina Mac OS X com portas Fink ou Mac, ele configurará o numpy para usar ATLAS ou o Accelerate Framework da Apple . Você pode verificar executando ldd no arquivo numpy.core._dotblas ou chamando numpy.show_config () .
Conclusões
MKL tem melhor desempenho, seguido de perto por GotoBlas2 .
No teste de autovalor , GotoBlas2 tem um desempenho surpreendentemente pior do que o esperado. Não sei por que esse é o caso.
O Accelerate Framework da Apple tem um desempenho muito bom, especialmente no modo single threaded (em comparação com as outras implementações BLAS).
Ambos GotoBlas2 e MKL escalam muito bem com o número de threads. Portanto, se você tiver que lidar com grandes matrizes, executá-lo em vários threads ajudará muito.
Em qualquer caso, não use a implementação padrão do netlib blas porque é muito lenta para qualquer trabalho computacional sério.
Em nosso cluster, também instalei o ACML da AMD e o desempenho foi semelhante ao MKL e GotoBlas2 . Eu não tenho nenhum número difícil.
Eu pessoalmente recomendaria usar GotoBlas2 porque é mais fácil de instalar e é grátis.
Se você quiser codificar em C ++ / C também verifique o Eigen3, que supostamente supera o MKL / GotoBlas2 em alguns casos e também é muito fácil de usar.
fonte
Aqui está outra referência (no Linux, basta digitar
make
): http://dl.dropbox.com/u/5453551/blas_call_benchmark.ziphttp://dl.dropbox.com/u/5453551/blas_call_benchmark.png
Não vejo essencialmente nenhuma diferença entre os diferentes métodos para grandes matrizes, entre Numpy, Ctypes e Fortran. (Fortran em vez de C ++ --- e se isso for importante, seu benchmark provavelmente está quebrado.)
SuaTalvez seu benchmark também tenha outros bugs, por exemplo, comparar entre diferentes bibliotecas BLAS ou diferentes configurações BLAS, como número de threads, ou entre tempo real e tempo de CPU?CalcTime
função em C ++ parece ter um erro de sinal.... + ((double)start.tv_usec))
deve ser em vez disso... - ((double)start.tv_usec))
.EDIT : falha ao contar as chaves na
CalcTime
função - está OK.Como uma diretriz: se você fizer um benchmark, sempre poste todo o código em algum lugar. Comentar sobre benchmarks, especialmente quando surpreendente, sem ter o código completo geralmente não é produtivo.
Para descobrir contra qual BLAS Numpy está vinculado, faça:
ATUALIZAÇÃO : Se você não pode importar numpy.core._dotblas, seu Numpy está usando sua cópia interna do BLAS, que é mais lenta, e não deve ser usada em computação de desempenho! A resposta de @Woltan abaixo indica que esta é a explicação para a diferença que ele vê em Numpy vs. Ctypes + BLAS.
Para corrigir a situação, você precisa de ATLAS ou MKL --- verifique estas instruções: http://scipy.org/Installing_SciPy/Linux A maioria das distribuições Linux vem com ATLAS, então a melhor opção é instalar o
libatlas-dev
pacote (o nome pode variar) .fonte
import numpy.core._dotblas
. Qual pode ser o problema aqui? Vou tentar limpar meu benchmark e escrever um makefile para que outros possam testá-lo.otool -L
lugar doldd
LinuxDado o rigor que você mostrou com sua análise, estou surpreso com os resultados até agora. Eu coloquei isso como uma 'resposta', mas apenas porque é muito longo para um comentário e fornece uma possibilidade (embora eu espere que você tenha considerado).
Eu teria pensado que a abordagem numpy / python não acrescentaria muito overhead para uma matriz de complexidade razoável, já que à medida que a complexidade aumenta, a proporção em que o python participa deve ser pequena. Estou mais interessado nos resultados do lado direito do gráfico, mas a discrepância de ordens de magnitude mostrada lá seria preocupante.
Eu me pergunto se você está usando os melhores algoritmos que o entorpecimento pode aproveitar. Do guia de compilação para Linux:
"Build FFTW (3.1.2): Versões SciPy> = 0.7 e Numpy> = 1.2: Por causa dos problemas de licença, configuração e manutenção, o suporte para FFTW foi removido nas versões de SciPy> = 0.7 e NumPy> = 1.2. Agora usa uma versão integrada do fftpack. Existem algumas maneiras de aproveitar a velocidade do FFTW, se necessário, para sua análise. Faça downgrade para uma versão Numpy / Scipy que inclua suporte. Instale ou crie seu próprio wrapper do FFTW. Consulte http: //developer.berlios.de/projects/pyfftw/ como um exemplo não aprovado. "
Você compilou numpy com mkl? ( http://software.intel.com/en-us/articles/intel-mkl/ ). Se você estiver executando no Linux, as instruções para compilar numpy com mkl estão aqui: http://www.scipy.org/Installing_SciPy/Linux#head-7ce43956a69ec51c6f2cedd894a4715d5bfff974 (apesar de url). A parte principal é:
Se você estiver no Windows, pode obter um binário compilado com mkl (e também obter o pyfftw e muitos outros algoritmos relacionados) em: http://www.lfd.uci.edu/~gohlke/pythonlibs/ , com um dívida de gratidão para com Christoph Gohlke no Laboratory for Fluorescence Dynamics, UC Irvine.
Advertência, em qualquer caso, há muitos problemas de licenciamento e assim por diante para estar ciente, mas a página Intel explica esses. Novamente, imagino que você tenha considerado isso, mas se você atender aos requisitos de licenciamento (o que no Linux é muito fácil de fazer), isso aceleraria muito a parte entorpecente em relação ao uso de uma construção automática simples, sem nem mesmo FFTW. Terei interesse em seguir este tópico e ver o que os outros pensam. Apesar de tudo, excelente rigor e excelente questão. Obrigado por postar.
fonte