Более 3 лет прошло с тех пор, как вопрос был опубликован, и за это время был достигнут значительный прогресс.По этому коду (Обновление 2 вопроса):
# cython: infer_types=True
# cython: boundscheck=False
# cython: wraparound=False
import numpy as np
cimport numpy as np
cdef inline inc(np.ndarray[np.int32_t, ndim=2] arr, int i, int j):
arr[i, j]+= 1
def test1(np.ndarray[np.int32_t, ndim=2] arr):
cdef int i,j
for i in xrange(arr.shape[0]):
for j in xrange(arr.shape[1]):
inc(arr, i, j)
def test2(np.ndarray[np.int32_t, ndim=2] arr):
cdef int i,j
for i in xrange(arr.shape[0]):
for j in xrange(arr.shape[1]):
arr[i,j] += 1
Я получаю следующие тайминги:
arr = np.zeros((1000,1000), dtype=np.int32)
%timeit test1(arr)
%timeit test2(arr)
1 loops, best of 3: 354 ms per loop
1000 loops, best of 3: 1.02 ms per loop
Так что проблема воспроизводима даже спустя более 3 лет.Cython теперь имеет типизированных просмотров памяти , AFAIK он был введен в Cython 0.16, поэтому недоступен на момент публикации вопроса.С этим:
# cython: infer_types=True
# cython: boundscheck=False
# cython: wraparound=False
import numpy as np
cimport numpy as np
cdef inline inc(int[:, ::1] tmv, int i, int j):
tmv[i, j]+= 1
def test3(np.ndarray[np.int32_t, ndim=2] arr):
cdef int i,j
cdef int[:, ::1] tmv = arr
for i in xrange(tmv.shape[0]):
for j in xrange(tmv.shape[1]):
inc(tmv, i, j)
def test4(np.ndarray[np.int32_t, ndim=2] arr):
cdef int i,j
cdef int[:, ::1] tmv = arr
for i in xrange(tmv.shape[0]):
for j in xrange(tmv.shape[1]):
tmv[i,j] += 1
С этим я получаю:
arr = np.zeros((1000,1000), dtype=np.int32)
%timeit test3(arr)
%timeit test4(arr)
1000 loops, best of 3: 977 µs per loop
1000 loops, best of 3: 838 µs per loop
Мы почти там и уже быстрее, чем старомодный способ!Теперь функция inc()
может быть объявлена nogil
, поэтому давайте объявим это так!Но упс:
Error compiling Cython file:
[...]
cdef inline inc(int[:, ::1] tmv, int i, int j) nogil:
^
[...]
Function with Python return type cannot be declared nogil
А-а-а, я полностью пропустил, что отсутствует тип возврата void
!Еще раз, но теперь с void
:
cdef inline void inc(int[:, ::1] tmv, int i, int j) nogil:
tmv[i, j]+= 1
И, наконец, я получаю:
%timeit test3(arr)
%timeit test4(arr)
1000 loops, best of 3: 843 µs per loop
1000 loops, best of 3: 853 µs per loop
Так же быстро, как ручная установка!
Теперь просторади интереса я попытался Numba на этом коде:
import numpy as np
from numba import autojit, jit
@autojit
def inc(arr, i, j):
arr[i, j] += 1
@autojit
def test5(arr):
for i in xrange(arr.shape[0]):
for j in xrange(arr.shape[1]):
inc(arr, i, j)
Я получаю:
arr = np.zeros((1000,1000), dtype=np.int32)
%timeit test5(arr)
100 loops, best of 3: 4.03 ms per loop
Даже если он в 4,7 раза медленнее, чем Cython, скорее всего, потому чтоJIT-компилятору не удалось встроить inc()
, я думаю, что это УДИВИТЕЛЬНО! Все, что мне нужно было сделать, это добавить @autojit
и не нужно было путать код с неуклюжими объявлениями типов;88-кратное ускорение почти за ничто!
Я пробовал другие вещи с Numba, такие как
@jit('void(i4[:],i4,i4)')
def inc(arr, i, j):
arr[i, j] += 1
или nopython=True
, но не смог улучшить его.
Улучшение встраивания находится в списке разработчиков Numba , нам нужно только подать больше запросов, чтобы сделать его более приоритетным.;)