Я хотів би написати програму, яка широко використовує функції лінійної алгебри BLAS та LAPACK. Оскільки результативність - це питання, я зробив деякий бенчмаркінг і хотів би знати, чи підхід, який я застосував, законний
У мене є, так би мовити, три учасники та хочу перевірити їхню роботу простим множенням на матрицю-матрицю. Учасники змагань:
- Numpy, використовуючи лише функціональні можливості
dot
. - Python, виклик функцій BLAS через спільний об'єкт.
- C ++, виклик функцій BLAS через спільний об'єкт.
Сценарій
Я реалізував множення на матрицю-матрицю для різних вимірів i
. i
працює від 5 до 500 з кроком 5 і матриць m1
і m2
встановлюються так:
m1 = numpy.random.rand(i,i).astype(numpy.float32)
m2 = numpy.random.rand(i,i).astype(numpy.float32)
1. Пустотливий
Використовуваний код виглядає приблизно так:
tNumpy = timeit.Timer("numpy.dot(m1, m2)", "import numpy; from __main__ import m1, m2")
rNumpy.append((i, tNumpy.repeat(20, 1)))
2. Python, викликаючи BLAS через спільний об'єкт
З функцією
_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))
тестовий код виглядає приблизно так:
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 через спільний об'єкт
Тепер код c ++, природно, трохи довший, тому я скорочую інформацію до мінімуму.
Я завантажую функцію
void* handle = dlopen("libblas.so", RTLD_LAZY);
void* Func = dlsym(handle, "sgemm_");
Я вимірюю час 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 разів. Я обчислюю час, пройдений з
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 ++ та python? Обидва використовують спільні об'єкти для своїх розрахунків.
- Оскільки я вважаю за краще використовувати python для своєї програми, що я можу зробити для підвищення продуктивності під час виклику підпрограм BLAS або LAPACK?
Завантажити
Повний тест можна завантажити тут . (JF Себастьян зробив це посилання можливим ^^)
r
матриці несправедливий. Я вирішую "питання" зараз і публікую нові результати.
np.ascontiguousarray()
(врахуйте порядок C проти Fortran). 2. переконайтеся, що np.dot()
використовує те саме libblas.so
.
m1
і m2
має ascontiguousarray
прапор як True
. І numpy використовує той самий спільний об'єкт, що і C. Щодо порядку масиву: На даний момент я не зацікавлений результатом обчислення, отже, порядок не має значення.