Cython эффективный цикл ‘for’ для заданного списка вместо диапазона (N)

#python #python-2.7 #optimization #cython

#python #python-2.7 #оптимизация #cython

Вопрос:

Я пишу на cython (python 2.7) и имею дело с циклами «для». Пока я использую стандарт for i in range(N) , я получил классный код: нет желтого предупреждения на cythonized code.html .

Когда я создаю список целых чисел (как range (N), не так ли?), Например:

 cdef long [:] lista = np.array(list(nx.node_connected_component(Graph, v)))
  

который дает мне список всех индексов узлов в связном компоненте v в графе Graph . Я получил желтое предупреждение, когда пытаюсь определить for i in lista: :

   __pyx_t_1 = __pyx_memoryview_fromslice(__pyx_v_lista, 1, (PyObject *(*)(char *)) __pyx_memview_get_long, (int (*)(char *, PyObject *)) __pyx_memview_set_long, 0);; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 151, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (likely(PyList_CheckExact(__pyx_t_1)) || PyTuple_CheckExact(__pyx_t_1)) {
    __pyx_t_6 = __pyx_t_1; __Pyx_INCREF(__pyx_t_6); __pyx_t_15 = 0;
    __pyx_t_17 = NULL;
  } else {
    __pyx_t_15 = -1; __pyx_t_6 = PyObject_GetIter(__pyx_t_1); if (unlikely(!__pyx_t_6)) __PYX_ERR(0, 151, __pyx_L1_error)
    __Pyx_GOTREF(__pyx_t_6);
    __pyx_t_17 = Py_TYPE(__pyx_t_6)->tp_iternext; if (unlikely(!__pyx_t_17)) __PYX_ERR(0, 151, __pyx_L1_error)
  }
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  for (;;) {
    if (likely(!__pyx_t_17)) {
      if (likely(PyList_CheckExact(__pyx_t_6))) {
        if (__pyx_t_15 >= PyList_GET_SIZE(__pyx_t_6)) break;
        #if CYTHON_COMPILING_IN_CPYTHON
        __pyx_t_1 = PyList_GET_ITEM(__pyx_t_6, __pyx_t_15); __Pyx_INCREF(__pyx_t_1); __pyx_t_15  ; if (unlikely(0 < 0)) __PYX_ERR(0, 151, __pyx_L1_error)
        #else
        __pyx_t_1 = PySequence_ITEM(__pyx_t_6, __pyx_t_15); __pyx_t_15  ; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 151, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_1);
        #endif
      } else {
        if (__pyx_t_15 >= PyTuple_GET_SIZE(__pyx_t_6)) break;
        #if CYTHON_COMPILING_IN_CPYTHON
        __pyx_t_1 = PyTuple_GET_ITEM(__pyx_t_6, __pyx_t_15); __Pyx_INCREF(__pyx_t_1); __pyx_t_15  ; if (unlikely(0 < 0)) __PYX_ERR(0, 151, __pyx_L1_error)
        #else
        __pyx_t_1 = PySequence_ITEM(__pyx_t_6, __pyx_t_15); __pyx_t_15  ; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 151, __pyx_L1_error)
        __Pyx_GOTREF(__pyx_t_1);
        #endif
      }
    } else {
      __pyx_t_1 = __pyx_t_17(__pyx_t_6);
      if (unlikely(!__pyx_t_1)) {
        PyObject* exc_type = PyErr_Occurred();
        if (exc_type) {
          if (likely(exc_type == PyExc_StopIteration || PyErr_GivenExceptionMatches(exc_type, PyExc_StopIteration))) PyErr_Clear();
          else __PYX_ERR(0, 151, __pyx_L1_error)
        }
        break;
      }
      __Pyx_GOTREF(__pyx_t_1);
    }
    __pyx_t_2 = __Pyx_PyInt_As_int(__pyx_t_1); if (unlikely((__pyx_t_2 == (int)-1) amp;amp; PyErr_Occurred())) __PYX_ERR(0, 151, __pyx_L1_error)
    __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
    __pyx_v_i = __pyx_t_2;
/* … */
  }
  __Pyx_DECREF(__pyx_t_6); __pyx_t_6 = 0;
  

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

Какое правильное назначение для lista ?

Комментарии:

1. Что nx.node_connected_component возвращает? Это может определить, что вам нужно делать.

2. @DavidW возвращает набор.

Ответ №1:

Вам лучше просто перебирать его по индексу

 cdef Py_ssize_t i
cdef long val
with cython.boundscheck(False): # these two lines are optional but may increase speed
  with cython.wraparound(False):
    for i in range(lista.shape[0]):
      val = lista[i]
  

Другая оптимизация, которую вы, вероятно, могли бы сделать, — это определить lista как

 cdef long [::1] lista
  

в котором говорится, что он непрерывен в памяти.

(Мое первоначальное прочтение вопроса заставило меня подумать, что речь идет о преобразовании из a set : np.array(list(nx.node_connected_component(Graph, v))) . Я не думаю, что это то, о чем вы спрашиваете, но в случае, если это так, я не вижу способа ускорить эту строку.)

Комментарии:

1. Я не понимаю одну вещь. Почему мы определяем val ? Это важно или я могу просто использовать lista[i] ? И в чем идея cdef Py_ssize_t ? Спасибо

2. Вы можете просто использовать lista[i] . Py_ssize_t это целое число со знаком, которое достаточно велико, чтобы использовать его для индексации в любой массив, который может выделить Python. Вы можете использовать любой целочисленный тип, который вам нравится, но если lista он становится слишком длинным, вы можете не дойти до конца.