Tôi muốn viết một chương trình sử dụng rộng rãi các hàm đại số tuyến tính BLAS và LAPACK. Vì hiệu suất là một vấn đề, tôi đã thực hiện một số điểm chuẩn và muốn biết, nếu cách tiếp cận tôi đã thực hiện là hợp pháp.
Có thể nói, tôi có ba thí sinh và muốn kiểm tra hiệu suất của họ bằng một phép nhân ma trận đơn giản. Các thí sinh là:
- Numpy, chỉ sử dụng chức năng của
dot
. - Python, gọi các chức năng BLAS thông qua một đối tượng được chia sẻ.
- C ++, gọi các chức năng BLAS thông qua một đối tượng được chia sẻ.
Tình huống
Tôi đã thực hiện phép nhân ma trận cho các chiều khác nhau i
. i
chạy từ 5 đến 500 với gia số là 5 và các ma trận m1
và m2
được thiết lập như sau:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Numpy
Mã được sử dụng trông giống như sau:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, gọi BLAS thông qua một đối tượng được chia sẻ
Với chức năng
_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))
mã kiểm tra trông như thế này:
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 ++, gọi BLAS thông qua một đối tượng được chia sẻ
Bây giờ mã c ++ tự nhiên dài hơn một chút vì vậy tôi giảm thông tin xuống mức tối thiểu.
Tôi tải chức năng với
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Tôi đo thời gian với gettimeofday
như thế này:
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);
đâu j
là một vòng lặp chạy 20 lần. Tôi tính toán thời gian trôi qua với
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;
}
Các kết quả
Kết quả được hiển thị trong biểu đồ dưới đây:
Câu hỏi
- Bạn có nghĩ rằng cách tiếp cận của tôi là công bằng hay có một số chi phí không cần thiết mà tôi có thể tránh?
- Bạn có mong đợi rằng kết quả sẽ cho thấy sự khác biệt rất lớn giữa phương pháp c ++ và python không? Cả hai đều đang sử dụng các đối tượng được chia sẻ để tính toán.
- Vì tôi muốn sử dụng python cho chương trình của mình, tôi có thể làm gì để tăng hiệu suất khi gọi quy trình BLAS hoặc LAPACK?
Tải xuống
Điểm chuẩn hoàn chỉnh có thể được tải xuống tại đây . (JF Sebastian đã biến liên kết đó thành khả thi ^^)
r
ma trận là không công bằng. Tôi đang giải quyết "vấn đề" ngay bây giờ và đăng kết quả mới.
np.ascontiguousarray()
(xem xét thứ tự C so với Fortran). 2. chắc chắn rằng np.dot()
sử dụng như nhau libblas.so
.
m1
và m2
có ascontiguousarray
cờ là True
. Và numpy sử dụng cùng một đối tượng được chia sẻ như C. Đối với thứ tự của mảng: Hiện tại tôi không quan tâm đến kết quả tính toán nên thứ tự không liên quan.