Analiza porównawcza (python vs. c++ przy użyciu BLAS) i (numpy)

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

Chciałbym napisać program, który szeroko wykorzystuje funkcjonalności algebry liniowej BLAS i LAPACK. Ponieważ wydajność jest problemem, przeprowadziłem analizę porównawczą i chciałbym wiedzieć, czy przyjęte przeze mnie podejście jest uzasadnione.

Mam, że tak powiem, trzech zawodników i chcę przetestować ich wydajność za pomocą prostej matrycy macierzy mnożenie. Zawodnikami są:

  1. Numpy, korzystający tylko z funkcjonalności kropki.
  2. Python, wywołujący funkcje BLAS przez wspólny obiekt.
  3. C++, wywoływanie funkcjonalności BLAS przez obiekt współdzielony.

Scenariusz

Zaimplementowałem mnożenie macierzy dla różnych wymiarów ja. i działa od 5 do 500 z przyrostem 5, a macierze m1 i m2 są skonfigurowane w następujący sposób:

m1 = numpy.random.rand(i,i).astype(numpy.float32) m2 = numpy.random.rand(i,i).astype(numpy.float32) 

1. Numpy

Użyty kod wygląda tak:

tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1 , m2") rNumpy.append((i, tNumpy.repeat(20, 1))) 

2. Python, wywoływanie BLAS przez obiekt współdzielony

Za pomocą funkcji

_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)) 

kod testu wygląda tak:

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++, wywoływanie BLAS przez obiekt współdzielony

Teraz kod c++ jest oczywiście trochę dłuższy, więc ograniczam informacje do minimum.
Ładuję funkcję za pomocą

unieważniony* uchwyt = dlopen("libblas.so", RTLD_LAZY); void* Func = dlsym(uchwyt, "sgemm_"); 

Mierzę czas za pomocą gettimeofday w ten sposób:

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(początek, koniec); 

gdzie j jest pętlą wykonaną 20 razy. Obliczam czas, który minął za pomocą

double CalcTime (początek przedziału czasu, koniec przedziału czasu) { double factor = 1000000; return (((double)end.tv_sec) * factor + ((double)end.tv_usec) - (((double)start.tv_sec) * factor + ((double)start.tv_usec))) / factor; } 

Wyniki

Wynik jest pokazany na poniższym wykresie:

wprowadź opis obrazu tutaj

Pytania

  1. Czy uważasz, że moje podejście jest sprawiedliwe, czy może mam jakieś niepotrzebne koszty ogólne uniknąć?
  2. Czy spodziewałbyś się, że wynik pokaże tak dużą rozbieżność między podejściem C++ i Pythona? Oba używają obiektów współdzielonych do swoich obliczeń.
  3. Ponieważ wolałbym używać Pythona w moim programie, co mogę zrobić, aby zwiększyć wydajność podczas wywoływania procedur BLAS lub LAPACK?

Pobierz

Pełny test porównawczy można pobrać tutaj. (JF Sebastian umożliwił ten link^^)