我試圖找出做矩陣乘法的最快方法,並試圖三種方式:爲什麼矩陣乘numpy的速度要快於Python中的ctypes?
- 純Python實現:這裏沒有驚喜。
- 使用
numpy.dot(a, b)
- Numpy實現在Python中使用模塊與C進行接口。
這是轉變爲一個共享庫中的C代碼:
#include <stdio.h>
#include <stdlib.h>
void matmult(float* a, float* b, float* c, int n) {
int i = 0;
int j = 0;
int k = 0;
/*float* c = malloc(nay * sizeof(float));*/
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
int sub = 0;
for (k = 0; k < n; k++) {
sub = sub + a[i * n + k] * b[k * n + j];
}
c[i * n + j] = sub;
}
}
return ;
}
而Python代碼調用它:
def C_mat_mult(a, b):
libmatmult = ctypes.CDLL("./matmult.so")
dima = len(a) * len(a)
dimb = len(b) * len(b)
array_a = ctypes.c_float * dima
array_b = ctypes.c_float * dimb
array_c = ctypes.c_float * dima
suma = array_a()
sumb = array_b()
sumc = array_c()
inda = 0
for i in range(0, len(a)):
for j in range(0, len(a[i])):
suma[inda] = a[i][j]
inda = inda + 1
indb = 0
for i in range(0, len(b)):
for j in range(0, len(b[i])):
sumb[indb] = b[i][j]
indb = indb + 1
libmatmult.matmult(ctypes.byref(suma), ctypes.byref(sumb), ctypes.byref(sumc), 2);
res = numpy.zeros([len(a), len(a)])
indc = 0
for i in range(0, len(sumc)):
res[indc][i % len(a)] = sumc[i]
if i % len(a) == len(a) - 1:
indc = indc + 1
return res
我想有一個用C版本的賭注本來會更快......而且我輸了!下面是我的標杆,這似乎表明,我不是這樣做是錯誤的,或者說是numpy
愣神快:
我想明白爲什麼numpy
版本比版本快,我我甚至沒有談論純粹的Python實現,因爲它很明顯。
不錯的問題 - 事實證明,np.dot()比C中天真的GPU實現還要快。 – user2398029
讓您的幼稚C matmul運行緩慢的最大因素之一是內存訪問模式。 'b [k * n + j];'在內部循環內(在'k'之上)有一個'n'的跨度,所以它在每次訪問時觸及不同的緩存行。而你的循環無法自動矢量化SSE/AVX。 **解決這個問題的方法是先轉換'b',這會花費O(n^2)時間,並且在從'b'執行O(n^3)加載時減少緩存未命中時支付自己的代價。**這仍然會盡管沒有高速緩存阻塞(又稱循環平鋪)是一個天真的實現。 –
由於您使用'int sum'(出於某種原因...),如果內部循環訪問兩個連續數組,則您的循環實際上可以不使用'-fast-math'進行矢量化。 FP數學不是關聯的,所以編譯器不能在沒有'-fast-math'的情況下重新排序操作,但是整數數學是關聯的(並且比FP添加的延遲更低,這有助於如果你不打算優化循環多個累加器或其他延遲隱藏的東西)。 'float' - >'int'的轉換成本與FP'add'大致相同(實際上在Intel CPU上使用FP加ALU),所以在優化代碼中不值得。 –