亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

為什么np.where()在數組切片的副本上比在原始數組上的視圖更快?

為什么np.where()在數組切片的副本上比在原始數組上的視圖更快?

慕容708150 2021-04-06 09:26:44
我正在分析一些代碼,發現結果令我感到驚訝np.where()。我想where()在數組的一部分上使用(知道2D數組的很大一部分與我的搜索無關),并發現它是我代碼中的瓶頸。作為測試,我創建了一個新的2D數組作為該切片的副本,并測試了的速度where()。事實證明,它的運行速度明顯更快。在我的實際情況中,速度的提高確實非常顯著,但是我認為此測試代碼仍然可以證明我的發現:import numpy as npdef where_on_view(arr):    new_arr = np.where(arr[:, 25:75] == 5, arr[:, 25:75], np.NaN)def where_on_copy(arr):    copied_arr = arr[:, 25:75].copy()    new_arr = np.where(copied_arr == 5, copied_arr, np.NaN)arr = np.random.choice(np.arange(10), 1000000).reshape(1000, 1000)而timeit結果:%timeit where_on_view(arr)398 μs ± 2.82 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)%timeit where_on_copy(arr)295 μs ± 6.07 μs per loop (mean ± std. dev. of 7 runs, 1000 loops each)由于這兩種方法都返回一個新數組,因此我不清楚如何事先獲取切片的完整副本才能達到np.where()這種程度。我還進行了一些健全性檢查,以確認:在這種情況下,它們都返回相同的結果。where() 搜索實際上僅限于切片,而不是檢查整個數組,然后過濾輸出。這里:# Sanity check that they do give the same outputtest_arr = np.random.choice(np.arange(3), 25).reshape(5, 5)test_arr_copy = test_arr[:, 1:3].copy()print("No copy")print(np.where(test_arr[:, 1:3] == 2, test_arr[:, 1:3], np.NaN))print("With copy")print(np.where(test_arr_copy == 2, test_arr_copy, np.NaN))# Sanity check that it doesn't search the whole arraydef where_on_full_array(arr):    new_arr = np.where(arr == 5, arr, np.NaN)#%timeit where_on_full_array(arr)#7.54 ms ± 47.6 μs per loop (mean ± std. dev. of 7 runs, 100 loops each)我很好奇這種情況下增加的開銷來自哪里?
查看完整描述

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使用的“完整迭代”相比,“原始迭代”的開銷相當小。


查看完整回答
反對 回復 2021-04-09
  • 1 回答
  • 0 關注
  • 219 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號