1 回答

TA貢獻1844條經驗 獲得超8個贊
以下是一些源代碼片段,這些片段至少部分地解釋了觀察結果。我沒有考慮,where因為差異似乎是以前創建的。相反,我ufuncs通常在看。
ufuncs的基本功能
暫時忽略一些特殊的套管函數,這是由覆蓋其他尺寸的外部循環內部可能進行了優化的最內層一維循環計算出來的。
外循環比較昂貴,它使用numpy nditer,因此必須設置它,并且對于每個迭代調用(iternext它是一個函數指針)都必須進行設置,因此沒有內聯。
相比之下,內部循環是一個簡單的C循環。
交錯的ufunc評估會產生大量開銷
來自numpy / core / src / umath / ufunc_object.c所包含的numpy / core / src / private / lowlevel_strided_loops.h
/*
* TRIVIAL ITERATION
*
* In some cases when the iteration order isn't important, iteration over
* arrays is trivial. This is the case when:
* * The array has 0 or 1 dimensions.
* * The array is C or Fortran contiguous.
* Use of an iterator can be skipped when this occurs. These macros assist
* in detecting and taking advantage of the situation. Note that it may
* be worthwhile to further check if the stride is a contiguous stride
* and take advantage of that.
因此,我們看到ufunc具有連續參數的a可以通過對快速內部循環的一次調用來評估,從而完全繞過外部循環。
為了理解復雜的差異,開銷看看功能trivial_two/three_operand_loopVSiterator_loop在numpy的/核心/ src目錄/ umath / ufunc_object.c和所有npyiter_iternext_*在numpy的功能/核心/ src目錄/多陣列/ nditer_templ.c
交錯的ufunc eval比交錯的副本更昂貴
從自動生成的numpy / core / src / multiarray / lowlevel_strided_loops.c
/*
* This file contains low-level loops for copying and byte-swapping
* strided data.
*
該文件將近25萬行。
相比之下,還自動生成的文件numpy / core / src / umath / loops.c提供了最里面的ufunc循環,大約只有1.5萬行。
這本身表明復制可能比ufunc評估更優化。
這里相關的是宏
/* Start raw iteration */
#define NPY_RAW_ITER_START(idim, ndim, coord, shape) \
memset((coord), 0, (ndim) * sizeof(coord[0])); \
do {
[...]
/* Increment to the next n-dimensional coordinate for two raw arrays */
#define NPY_RAW_ITER_TWO_NEXT(idim, ndim, coord, shape, \
dataA, stridesA, dataB, stridesB) \
for ((idim) = 1; (idim) < (ndim); ++(idim)) { \
if (++(coord)[idim] == (shape)[idim]) { \
(coord)[idim] = 0; \
(dataA) -= ((shape)[idim] - 1) * (stridesA)[idim]; \
(dataB) -= ((shape)[idim] - 1) * (stridesB)[idim]; \
} \
else { \
(dataA) += (stridesA)[idim]; \
(dataB) += (stridesB)[idim]; \
break; \
} \
} \
} while ((idim) < (ndim))
由raw_array_assign_arraynumpy / core / src / multiarray / array_assign_array.c中的函數使用,該函數為Pythonndarray.copy方法進行實際復制。
我們可以看到,與ufuncs使用的“完整迭代”相比,“原始迭代”的開銷相當小。
添加回答
舉報