Я хотел бы написать программу, которая широко использует функциональные возможности линейной алгебры BLAS и LAPACK. Поскольку производительность является проблемой, я сделал некоторый бенчмаркинг и хотел бы знать, если подход, который я принял, является законным.
У меня есть, так сказать, три участника и хочу проверить их производительность с помощью простого матрично-матричного умножения. Конкурсанты:
- Numpy, используя только функциональные возможности
dot
. - Python, вызывающий функции BLAS через общий объект.
- С++, вызывающий функции 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. Numpy
Используемый код выглядит так:
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. С++, вызов BLAS через общий объект
Теперь код С++, естественно, немного длиннее, поэтому я свожу информацию к минимуму.
Я загружаю функцию с помощью
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;
}
Результаты
Результат показан на следующем рисунке:
Вопросы
- Как вы думаете, мой подход справедлив или есть некоторые ненужные накладные расходы, которых я могу избежать?
- Ожидаете ли вы, что результат будет показывать такое огромное несоответствие между подходом С++ и python? Оба используют общие объекты для своих вычислений.
- Поскольку я предпочитаю использовать python для своей программы, что я могу сделать, чтобы повысить производительность при вызове процедур BLAS или LAPACK?
Скачать
Полный эталонный тест можно скачать здесь. (Дж. Ф. Себастьян сделал эту ссылку возможной ^^)