Mam prostą funkcję Cythona, która pobiera długość podglądu pamięci:

cdef int get_length(int[:] a):
    return len(a)

Kompiluję kod za pomocą dyrektywy annotate=True, aby zobaczyć, gdzie Cython ma pewną interakcję z Pythonem.

Wynikowy html zawiera ten tekst dla wiersza return len(a):

  __pyx_t_1 = __Pyx_MemoryView_Len(__pyx_v_a); 
  __pyx_r = __pyx_t_1;
  goto __pyx_L0;

Taka interakcja w Pythonie powinna być powolna. Czy istnieje sposób, aby zapobiec interakcji w języku Python? Próbowałem a.shape[0], ale to nie pomogło.

3
Ginger 10 grudzień 2018, 00:49

1 odpowiedź

Najlepsza odpowiedź

Nie powinieneś się tym martwić, __Pyx_MemoryView_Len jest tak szybki, jak to tylko możliwe, ponieważ jest zdefiniowany jako:

typedef struct {
  struct {{memview_struct_name}} *memview;
  char *data;
  Py_ssize_t shape[{{max_dims}}];
  ...
} {{memviewslice_name}};

// used for "len(memviewslice)"
#define __Pyx_MemoryView_Len(m) (m.shape[0])

Kolor tej linii nie jest żółty, ale żółtawy - co w większości oznacza, że ​​nie daje to czystego kodu C, ale że używana jest pewna funkcjonalność _Pyx_XXX, co często nie jest wcale złe dla wydajności.

Nie ma interakcji z Pythonem - po przejściu preprocesora C będzie to postrzegane przez kompilator C tak, jakbyś napisał:

return a.shape[0]

Co przy okazji prowadzi do białej linii.

Jeśli interesuje Cię również rozmiar wielowymiarowego widoku memów, to pytanie tak może być warte przeczytania.


Opisz pokazuje definicję funkcji w postaci żółtej linii. Jednak za „żółty” kolor odpowiada tylko koszt po załadowaniu modułu (i jest on opłacany jednorazowo, a nie za każdym razem, gdy funkcja jest wywoływana).

Spójrz na wynikowy kod C:

static int __pyx_f_9my_module_get_length(__Pyx_memviewslice __pyx_v_a) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("get_length", 0);

  /* "my_module.pyx":5
 * 
 * cdef int get_length(int[:] a):
 *     return a.shape[0]             # <<<<<<<<<<<<<<
 */
  __pyx_r = (__pyx_v_a.shape[0]);
  goto __pyx_L0;

  /* "my_module.pyx":4
 *     return 3. * a
 * 
 * cdef int get_length(int[:] a):             # <<<<<<<<<<<<<<
 *     return a.shape[0]
 */

  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

RefNannyXXXX jest aktywny tylko wtedy, gdy kompilacja jest zdefiniowana CYTHON_REFNANNY.< /a>

Istnieje również inny kod pokazany przez narzędzie annotate, który odpowiada cdef int get_length(int[:] a): :

/* … */
  __pyx_t_1 = __Pyx_PyDict_NewPresized(0); if (unlikely(!__pyx_t_1)) __PYX_ERR(1, 1, __pyx_L1_error)
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(1, 1, __pyx_L1_error)
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;

Jednak ten kod jest częścią __pyx_pymod_exec_XXXXX/PyInit_XXXXX i jest wywoływany tylko raz, gdy moduł jest ładowany. Właściwie nie jestem pewien, jak to się ma do get_length i dlaczego jest to potrzebne, ale ponieważ koszty są tak małe, nigdy nie przejmowałem się tym wystarczająco, aby się dowiedzieć.

2
ead 11 grudzień 2018, 08:43