Задача
Это связано с вопросом, который я задал здесь , о реверсировании двумерного numpy массива по столбцам со случайными индексами в строке. Например.
np.random.seed(0)
arr = np.repeat(np.arange(6)[np.newaxis], 100000, axis=0)
m, n = arr.shape
indices = np.sort(np.random.rand(m, n).argsort(1)[:,:2], axis=1).astype("int32").
# reverse
for idx, (i, j) in enumerate(reverse):
arr[idx, i:j+1] = arr[idx, i:j+1][::-1]
---------------------------------------------------------------------------------------------------------
# `arr` # `indices` # `output`
array([[0, 1, 2, 3, 4, 5], array([[3, 4], array([[0, 1, 2, 4, 3, 5],
[0, 1, 2, 3, 4, 5], [0, 3], [3, 2, 1, 0, 4, 5],
[0, 1, 2, 3, 4, 5], [2, 4], [0, 1, 4, 3, 2, 5],
..., ..., --> ...,
[0, 1, 2, 3, 4, 5], [3, 5], [0, 1, 2, 5, 4, 3],
[0, 1, 2, 3, 4, 5], [1, 4], [0, 4, 3, 2, 1, 5],
[0, 1, 2, 3, 4, 5]]) [0, 2]]) [2, 1, 0, 3, 4, 5]])
Я экспериментировал с версией Cython этой функции, приведенной ниже.
cpdef int[:,:] reverse_indices(int[:,:] arr, int[:,:] indices):
cdef:
Py_ssize_t idx, N = arr.shape[0]
int i, j
for idx in range(N):
i, j = indices[idx, 0], indices[idx, 1]
arr[idx, i:j + 1] = arr[idx, i:j + 1][::-1]
return arr
Но я обнаружил, что это "всего лишь" ~ в 8 раз быстрее, чем чистый python версия (47мс против 375мс). Я обнаружил, что линия arr[idx, i:j + 1] = arr[idx, i:j + 1][::-1]
была особенно медленной. Я удалил это и заменил следующим, чтобы выполнить обратную операцию.
for idx in range(N):
i, j = indices[idx, 0], indices[idx, 1]
while i < j + 1:
arr[idx,i], arr[idx,j] = arr[idx,j], arr[idx,i]
i += 1
j -= 1
return arr
Это решение было в ~ 157 раз быстрее, чем версия python (2,4 мс против 375 мс).
Вопрос
- Я обнаружил, что даже без какого-либо реверсирования (
[::-1]
), то есть arr[idx, i:j + 1] = arr[idx, i:j + 1]
, оказывает аналогичное влияние на производительность. Почему нарезка секции массива имеет такой значительный эффект по сравнению с индексацией отдельного элемента? - Есть ли лучший способ обратить вспять секцию массива / списка в Cython?