Обращение массива в Cython - PullRequest
0 голосов
/ 23 марта 2020

Задача

Это связано с вопросом, который я задал здесь , о реверсировании двумерного 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. Я обнаружил, что даже без какого-либо реверсирования ([::-1]), то есть arr[idx, i:j + 1] = arr[idx, i:j + 1], оказывает аналогичное влияние на производительность. Почему нарезка секции массива имеет такой значительный эффект по сравнению с индексацией отдельного элемента?
  2. Есть ли лучший способ обратить вспять секцию массива / списка в Cython?
...