numpy.dot в 100 раз медленнее, чем родной C ++ 11

У меня есть фон Matlab, и когда я купил год назад ноутбук, я тщательно выбрал тот, который обладает большой вычислительной мощностью, машина имеет 4 потока и предлагает мне 8 потоков с частотой 2,4 ГГц. Машина зарекомендовала себя как очень мощная, и, используя простые циклы parfor, я мог использовать все потоки процессора, с которыми я получил ускорение около 8 для многих задач и экспериментов.

В это прекрасное воскресенье я экспериментировал с numpy, люди часто говорят мне, что основной бизнес numpy эффективно реализован с использованием libblas и, возможно, даже с использованием нескольких ядер и библиотек, таких как OpenMP (с OpenMP вы можете создавать парфор-подобные циклы, используя прагму в стиле c ).

Это общий подход для многих числовых и машинных алгоритмов обучения, вы выражаете их с помощью дорогостоящих операций высокого уровня, таких как умножение матриц, но для удобства в дорогом языке высокого уровня, таком как Matlab и python. Более того, c (++) позволяет нам обходить GIL.

Крутая часть в том, что линейная алгебраическая обработка должна выполняться очень быстро в python, когда бы вы ни использовали numpy. У вас просто есть накладные расходы при вызове некоторых функций, но тогда, если вычисления за ними велики, это незначительно.

Итак, даже не касаясь темы о том, что не все может быть выражено в линейной алгебре или других пустых операциях, я изложил это:

t = time.time(); numpy.dot(range(100000000), range(100000000)); print(time.time() - t)
40.37656021118164

Таким образом, я за эти 40 секунд увидел, что ОДИН из 8 потоков на моей машине работал на 100%, а остальные — около 0%. Мне это не понравилось, но даже с одним работающим потоком я ожидал, что это будет выполняться примерно через 0 секунд. Точечный продукт выполняет 100M + и *, поэтому мы имеем 2400M / 100M = 24 такта в секунду для одного +, одного * и любых дополнительных затрат.

Тем не менее, алгоритму нужно 40 * 24 = приблизительно = 1000 тиков (!!!!!) для +, * и накладных расходов. Давайте сделаем это в C ++:

#include<iostream>

int main() {
unsigned long long result = 0;
for(unsigned long long i=0; i < 100000000; i++)
result += i * i;
std::cout << result << '\n';
}

БЛИЦ:

herbert@machine:~$ g++ -std=c++11 dot100M.cc
herbert@machine:~$ time ./a.out
662921401752298880

real    0m0.254s
user    0m0.254s
sys 0m0.000s

0,254 секунды, почти в 100 раз быстрее, чем numpy.dot.

Я подумал, что, возможно, генератор диапазона python3 — это медленная часть, поэтому я помешал реализации c ++ 11, сначала сохранив все 100M чисел в std :: vector (используя итеративные push_back-ы), а затем итерируя по нему. Это было намного медленнее, это заняло чуть меньше 4 секунд, что все еще в 10 раз быстрее.

Я установил свой numpy с помощью ‘pip3 install numpy’ в ubuntu, и он некоторое время начал компилироваться, используя как gcc, так и gfortran, более того, я видел упоминания о файлах blas-header, проходящих через выходные данные компилятора.

По какой причине numpy.dot так медленно работает?

1

Решение

Так что ваше сравнение несправедливо. В вашем примере с Python вы сначала генерируете два объекта диапазона, конвертируете их в numpy-массивы, а затем выполняете скалярный продукт. Расчет занимает наименьшую часть. Вот цифры для моего компьютера:

>>> t=time.time();x=numpy.arange(100000000);numpy.dot(x,x);print time.time()-t
1.28280997276

И без генерации массива:

>>> t=time.time();numpy.dot(x,x);print time.time()-t
0.124325990677

Для завершения C-версия занимает примерно столько же времени:

real    0m0.108s
user    0m0.100s
sys 0m0.007s
12

Другие решения

range генерирует список на основе ваших заданных параметров, где в качестве вашего for Цикл в C просто увеличивает число.

Я согласен, что с точки зрения вычислений довольно затратно тратить так много времени на создание одного списка — опять же, это большой список, и вы запрашиваете два из них 😉

РЕДАКТИРОВАТЬ: как упоминалось в комментариях range генерирует списки, а не массивы.

Попробуйте заменить ваш range метод с приращением while цикл или подобное и посмотреть, если вы получите более терпимые результаты.

1

По вопросам рекламы [email protected]