Я хотел бы написать программу, которая широко использует функции линейной алгебры BLAS и LAPACK. Поскольку производительность является проблемой, я провел сравнительный анализ и хотел бы знать, является ли выбранный мной подход законным.
У меня есть, так сказать, три участника, и я хочу проверить их производительность с помощью простой матрицы-матрицы. умножение. Участниками являются:
- Numpy, использующий только функциональные возможности
dot
. - Python, вызывающий функции BLAS через общий объект.
- C++, вызов функций BLAS через общий объект.
Сценарий
Я реализовал матричное умножение для разных измерений я.
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(один), m1.ctypes.data_as(ctypes.c_void_p), byref(n), m2.ctypes.data_as(ctypes.c_void_p), byref(n), byref(ноль) , 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(дескриптор, "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(начало, конец);
где 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 Sebastian сделал эту ссылку возможной^^)