簡體   English   中英

為什么原生python列表上的for循環比numpy數組上的for循環快

[英]Why is for loop on native python list faster than for loop on numpy array

我正在閱讀介紹高性能Python中的 numpy並在我自己的計算機上使用代碼的章節。 偶然地,我用for循環運行了numpy版本,發現與本地python循環相比,結果出奇地慢。

該代碼的簡化版本如下所示,其中我定義了一個0的2D數組X和另一個1的2D數組Y,然后將Y重復地添加到X,從概念上講X + =Y。

import time
import numpy as np

grid_shape = (1024, 1024)

def simple_loop_comparison():
    xmax, ymax = grid_shape

    py_grid = [[0]*ymax for x in range(xmax)]
    py_ones = [[1]*ymax for x in range(xmax)]

    np_grid = np.zeros(grid_shape)
    np_ones = np.ones(grid_shape)

    def add_with_loop(grid, add_grid, xmax, ymax):
        for x in range(xmax):
            for y in range(ymax):
                grid[x][y] += add_grid[x][y]

    repeat = 20
    start = time.time()
    for i in range(repeat):
        # native python: loop over 2D array
        add_with_loop(py_grid, py_ones, xmax, ymax)
    print('for loop with native list=', time.time()-start)

    start = time.time()
    for i in range(repeat):
        # numpy: loop over 2D array
        add_with_loop(np_grid, np_ones, xmax, ymax)
    print('for loop with numpy array=', time.time()-start)

    start = time.time()
    for i in range(repeat):
        # vectorized numpy operation
        np_grid += np_ones
    print('numpy vectorization=', time.time()-start)

if __name__ == "__main__":
    simple_loop_comparison()

結果看起來像:

# when repeat=10
for loop with native list= 2.545672655105591
for loop with numpy array= 11.622980833053589
numpy vectorization= 0.020279645919799805

# when repeat=20
for loop with native list= 5.195128440856934
for loop with numpy array= 23.241904258728027
numpy vectorization= 0.04613637924194336

我完全期望numpy向量化操作的性能優於其他兩個,但是我很驚訝地看到在numpy數組上使用for循環的結果比本地python列表要慢得多。 我的理解是,至少使用numpy數組,即使使用for循環,緩存也應相對較好地填充,並且在不進行向量化的情況下,其性能應優於列表。

關於numpy或我不了解的低級CPU /緩存/內存如何工作? 非常感謝你。

編輯:更改標題

一個更簡單的情況-對列表和數組的列表理解:

In [119]: x = list(range(1000000))
In [120]: timeit [i for i in x]
47.4 ms ± 634 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [121]: arr = np.array(x)
In [122]: timeit [i for i in arr]
131 ms ± 3.69 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

列表具有一個數據緩沖區,該緩沖區包含指向內存中其他對象的指針。 因此,對列表進行迭代或索引編制僅需要查找該指針並獲取對象:

In [123]: type(x[1000])
Out[123]: int

數組將其元素作為字節存儲在數據緩沖區中。 提取元素需要(快速)找到那些字節,然后將它們包裝在numpy對象中(根據dtype)。 這樣的對象類似於0d單元素數組(具有許多相同的屬性)。

In [124]: type(arr[1000])
Out[124]: numpy.int32

該索引不僅獲取數字,而且還會重新創建它。

我經常將對象dtype數組描述為增強列表或降級列表。 像列表一樣,它包含指向內存中其他位置的對象的指針,但不能通過append增長。 我們經常說它失去了數字數組的許多好處。 但是它的迭代速度介於其他兩個之間:

In [125]: arrO = np.array(x, dtype=object)
In [127]: type(arrO[1000])
Out[127]: int
In [128]: timeit [i for i in arrO]
74.5 ms ± 1.42 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

無論如何,我在其他SO答案中發現,如果必須迭代,請堅持使用列表。 而且,如果您從列表開始,那么堅持使用列表通常會更快。 如您numpy vector速度很快,但是創建數組需要花費時間,這可能會抵消任何節省的時間。

比較從此列表創建數組所需的時間,與從頭開始創建此類數組所需的時間(使用已編譯的numpy代碼):

In [129]: timeit np.array(x)
109 ms ± 1.97 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [130]: timeit np.arange(len(x))
1.77 ms ± 31.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

由於它們是涉及向numpy詢問數據指針,在那些指針位置檢索值,然后使用它們進行迭代的轉換。 python列表包含較少的步驟。 只有在可以內部迭代或執行矢量,矩陣數學然后返回並返回答案或指向答案數組的指針時,才會注意到Numpy速度增益。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM