[英]python memoryview slower than expected
鑒於Python的memoryview
接口與緩沖的協議可以幫助減少需要進行數據的拷貝臨時,我決定做在此基礎上它的一個快速測試的回答這個問題 。
import time
expressions = ['list(b[i:i+1000])',
'list(b[i:])',
'b[i:]'
]
size = 1000000
x = b'x'*size
mv = memoryview(x)
for e in expressions:
print(f"Expression: {e}")
for b in (x, mv):
l = len(b)
start = time.time()
for i in range(0, l, 1000):
eval(e)
end = time.time()
print(f"Size: {size}, {type(b).__name__}, time: {end-start}")
結果:
$ python c:\temp\test_memoryview.py
Expression: list(b[i:i+1000])
Size: 1000000, bytes, time: 0.021999597549438477
Size: 1000000, memoryview, time: 0.03600668907165527
Expression: list(b[i:])
Size: 1000000, bytes, time: 5.3010172843933105
Size: 1000000, memoryview, time: 11.202003479003906
Expression: b[i:]
Size: 1000000, bytes, time: 0.2990117073059082
Size: 1000000, memoryview, time: 0.006985902786254883
前兩個結果似乎令人驚訝。 我知道調用列表將涉及數據的副本,但是我認為在切片內存視圖而不是基礎字節數組時,您可以保存臨時副本。
您無法像C或C ++那樣想到Python。 額外副本的常量因子開銷遠低於支持所有Python動態特性所涉及的常量因子開銷,尤其是在CPython中沒有JIT的情況下。 一旦考慮到要避免使用該副本而必須更改的其他內容,您就不能認為保存一個副本實際上會有所幫助。
在這種情況下,幾乎所有工作都在列表轉換中。 您要保存的副本毫無意義。 比較b[i:]
和list(b[i:])
,您會發現切片僅占運行時的百分之幾,即使切片執行復制也是如此。
保存的副本無關緊要,因為它基本上只是memcpy
。 相比之下,列表轉換需要在字節串或memoryview上創建一個迭代器,重復調用迭代器的tp_iternext
插槽,獲取與內存的原始字節對應的int
對象,等等,這更加昂貴。 對於memoryview來說,這甚至會更加昂貴,因為memoryview對象必須支持多維形狀和非字節數據類型,並且因為memoryview實現沒有專用的__iter__
實現,所以它經歷了基於序列的通用后備迭代,比較慢
您可以使用memoryview的tolist
方法而不是調用list
來節省一些時間。 這樣可以避免一堆迭代協議的開銷,並允許僅執行一次檢查,而不是每個項目一次。 在我的測試中,這幾乎和調用字節串上的list
一樣快。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.