Почему einsum numpy быстрее встроенных функций numpy?

Давайте начнем с трех массивовdtype=np.double, Синхронизация выполняется на процессоре Intel с использованием numpy 1.7.1, скомпилированной сicc и связано с Intelmkl, Процессор AMD с numpy 1.6.1, скомпилированный сgcc безmkl был также использован для проверки времени. Обратите внимание, что временные рамки масштабируются почти линейно в зависимости от размера системы и не связаны с небольшими накладными расходами, возникающими при использовании функций numpyif Заявления, эти различия будут отображаться в микросекундах, а не в миллисекундах:

arr_1D=np.arange(500,dtype=np.double)
large_arr_1D=np.arange(100000,dtype=np.double)
arr_2D=np.arange(500**2,dtype=np.double).reshape(500,500)
arr_3D=np.arange(500**3,dtype=np.double).reshape(500,500,500)

Сначала давайте посмотрим наnp.sum функция:

np.all(np.sum(arr_3D)==np.einsum('ijk->',arr_3D))
True

%timeit np.sum(arr_3D)
10 loops, best of 3: 142 ms per loop

%timeit np.einsum('ijk->', arr_3D)
10 loops, best of 3: 70.2 ms per loop

Полномочия:

np.allclose(arr_3D*arr_3D*arr_3D,np.einsum('ijk,ijk,ijk->ijk',arr_3D,arr_3D,arr_3D))
True

%timeit arr_3D*arr_3D*arr_3D
1 loops, best of 3: 1.32 s per loop

%timeit np.einsum('ijk,ijk,ijk->ijk', arr_3D, arr_3D, arr_3D)
1 loops, best of 3: 694 ms per loop

Наружный продукт:

np.all(np.outer(arr_1D,arr_1D)==np.einsum('i,k->ik',arr_1D,arr_1D))
True

%timeit np.outer(arr_1D, arr_1D)
1000 loops, best of 3: 411 us per loop

%timeit np.einsum('i,k->ik', arr_1D, arr_1D)
1000 loops, best of 3: 245 us per loop

Все вышеперечисленное в два раза быстрееnp.einsum, Это должны быть сравнения яблок с яблоками, так как все конкретноdtype=np.double, Я ожидал бы ускорение в операции как это:

np.allclose(np.sum(arr_2D*arr_3D),np.einsum('ij,oij->',arr_2D,arr_3D))
True

%timeit np.sum(arr_2D*arr_3D)
1 loops, best of 3: 813 ms per loop

%timeit np.einsum('ij,oij->', arr_2D, arr_3D)
10 loops, best of 3: 85.1 ms per loop

Einsum, кажется, как минимум вдвое быстрееnp.inner, np.outer, np.kron, а такжеnp.sum невзирая наaxes выбор. Основным исключением являетсяnp.dot как он вызывает DGEMM из библиотеки BLAS. Так почему жеnp.einsum быстрее, что другие функции NumPy, которые эквивалентны?

Кейс DGEMM для полноты:

np.allclose(np.dot(arr_2D,arr_2D),np.einsum('ij,jk',arr_2D,arr_2D))
True

%timeit np.einsum('ij,jk',arr_2D,arr_2D)
10 loops, best of 3: 56.1 ms per loop

%timeit np.dot(arr_2D,arr_2D)
100 loops, best of 3: 5.17 ms per loop

Ведущая теория от комментария @sebergs, чтоnp.einsum может использоватьSSE2Уфунки NumPy не будут до NumPy 1,8 (см.журнал изменений). Я считаю, что это правильный ответ, но естьне смог подтвердить это. Некоторое ограниченное доказательство можно найти, изменив dtype входного массива и наблюдая разницу скоростей, а также тот факт, что не все соблюдают одинаковые тенденции во времени.

Ответы на вопрос(3)

Ваш ответ на вопрос