Gostaria de escrever um programa que faça uso extensivo das funcionalidades de álgebra linear BLAS e LAPACK. Como o desempenho é um problema, fiz alguns benchmarks e gostaria de saber se a abordagem que tomei é legítima.
Tenho, por assim dizer, três concorrentes e quero testar seu desempenho com uma matriz-matriz simples multiplicação. Os concorrentes são:
- Numpy, fazendo uso apenas da 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 e
. i
é executado 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 é assim:
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 através 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) um = c_float(1.0) zero = c_float(0.0) _blaslib.sgemm_(byref(no_trans), byref(no_trans), byref(n), byref (n), byref(n), byref(um), 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 se parece com isso:
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 através de um objeto compartilhado
Agora o código c++ naturalmente é um pouco mais longo, então reduzo as informações ao mínimo.
Carrego a função com
void* handle = dlopen("libblas.so", RTLD_LAZY); void* Func = dlsym(handle, "sgemm_");
Eu meço o tempo com gettimeofday
assim:
gettimeofday(&start, NULL); f(&no_trans, &no_trans, &dim, &dim, &dim, &um, A, &dim, B, &dim, &zero, Return, &dim); gettimeofday(&fim, NULL); dTimes[j] = CalcTime(início, fim);
onde j
é um loop rodando 20 vezes. Eu calculo o tempo passado com
double CalcTime(timeval start, timeval end) { double factor = 1000000; return (((double)end.tv_sec) * fator + ((double)end.tv_usec) - (((double)start.tv_sec) * fator + ((double)start.tv_usec))) / fator; }
Resultados
O resultado é mostrado no gráfico abaixo:
Perguntas
- Você acha que minha abordagem é justa ou existem algumas despesas desnecessárias 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?
Download
O benchmark completo pode ser baixado aqui. (JF Sebastian tornou esse link possível^^)