Benchmarking (python vs. c++ usando BLAS) e (numpy)

| | | | | | | | | | | | | | | | | | | | | | | | | | |

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:

  1. Numpy, fazendo uso apenas da funcionalidade do dot.
  2. Python, chamando as funcionalidades do BLAS através de um objeto compartilhado.
  3. 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:

digite a descrição da imagem aqui

Perguntas

  1. Você acha que minha abordagem é justa ou existem algumas despesas desnecessárias que posso evitar?
  2. 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.
  3. 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^^)