BLAS ve LAPACK lineer cebir işlevlerini kapsamlı bir şekilde kullanan bir program yazmak istiyorum. Performans bir sorun olduğu için bazı kıyaslamalar yaptım ve benimsediğim yaklaşımın meşru olup olmadığını bilmek isterim.
Tabiri caizse üç yarışmacım var ve performanslarını basit bir matris-matris çarpımı ile test etmek istiyorum. Yarışmacılar:
- Numpy, yalnızca
dot
. - Python, BLAS işlevlerini paylaşılan bir nesne aracılığıyla çağırır.
- C ++, BLAS işlevlerini paylaşılan bir nesne aracılığıyla çağırır.
Senaryo
Farklı boyutlar için bir matris-matris çarpımı uyguladım i
. i
5 ve matricies artışıyla 5'ten 500'e kadar çalışır m1
ve m2
şu şekilde ayarlanır:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Uykulu
Kullanılan kod şuna benzer:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, BLAS'ı paylaşılan bir nesne aracılığıyla çağırma
İşlevi ile
_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))
test kodu şuna benzer:
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 ++, BLAS'ı paylaşılan bir nesne aracılığıyla çağırma
Artık c ++ kodu doğal olarak biraz daha uzundur, bu yüzden bilgiyi minimuma indiriyorum.
İşlevi yüklüyorum
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Zamanı şu şekilde ölçüyorum gettimeofday
:
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);
j
20 kez çalışan bir döngü nerede . İle geçen zamanı hesaplıyorum
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;
}
Sonuçlar
Sonuç aşağıdaki grafikte gösterilmektedir:
Sorular
- Yaklaşımımın adil olduğunu mu düşünüyorsunuz yoksa önleyebileceğim bazı gereksiz genel giderler var mı?
- Sonucun c ++ ve python yaklaşımı arasında bu kadar büyük bir tutarsızlık göstermesini bekler miydiniz? Her ikisi de hesaplamaları için paylaşılan nesneleri kullanıyor.
- Programım için python kullanmayı tercih ettiğim için, BLAS veya LAPACK rutinlerini çağırırken performansı artırmak için ne yapabilirim?
İndir
Karşılaştırmanın tamamı buradan indirilebilir . (JF Sebastian bu bağlantıyı mümkün kıldı ^^)
r
Matris için bellek tahsisi adil değil. Şu anda "sorunu" çözüyorum ve yeni sonuçları gönderiyorum.
np.ascontiguousarray()
(C'ye karşı Fortran sırasını düşünün). 2. np.dot()
Aynı şeyi kullandığından emin olun libblas.so
.
m1
ve m2
sahip ascontiguousarray
olduğu bayrak True
. Ve numpy, C ile aynı paylaşılan nesneyi kullanır. Dizinin sırasına gelince: Şu anda hesaplamanın sonucuyla ilgilenmiyorum, bu yüzden sıra alakasız.