2013-06-18 70 views
2

我試圖找出最有效的方式來創建一個長字節字符串(或bytearray)通過連接多個較短的字符串時,預先知道整個字符串的長度。我做了這個腳本,並與這些結果出來了:Python bytearray verses字節列表

import time 

MSG = b'test message' 
COUNT = 30000 

def bytes_list_test(): 
    tStart = time.clock() 
    l = [] 
    for i in range(COUNT): 
     l.append(MSG) 
    bs = b''.join(l) 
    print('byte list time:', time.clock() - tStart) 

def bytearray_test(): 
    tStart = time.clock() 
    ba = bytearray() 
    for i in range(COUNT): 
     for c in MSG: 
      ba.append(c) 
    print('array time:', time.clock() - tStart) 

def initialized_bytearray_test(): 
    tStart = time.clock() 
    ba = bytearray([0x00]*len(MSG)*COUNT) 
    for i in range(COUNT): 
     ba[i*len(MSG):i*len(MSG)+len(MSG)] = MSG 
    print('initialized array time:', time.clock() - tStart) 

bytes_list_test() 
bytearray_test() 
initialized_bytearray_test() 

結果:

byte list time:   0.0076534920117410365 
array time:    0.08107178658246994 
initialized array time: 0.08843219671325642 

幾個問題:

1)創建字節的列表,並使用join()方法結果所暗示的路要走嗎?

2)爲什麼使用一個字節列表比使用一個看起來像它是爲這種類型的事物設計的bytearray快得多? 3)你會認爲初始化的數組會比未初始化的數組快,因爲初始化的數組不需要調整大小(注意,它偶爾會執行得更好,但不會太多且不一致)。由於切片操作,速度不是很快嗎?

回答

2

第一個函數創建指向同一對象的指針列表(不是字節列表),然後join將執行一個內存分配,並且COUNT調用memcpy

您可以通過刪除臨時表和使用itertools.repeat做出更快的第一功能的時間(在我的測試5次):

def bytes_list_test_opt(): 
    tStart = time.clock() 
    bs = b''.join(itertools.repeat(MSG, COUNT)) 
    print('byte list opt time:', time.clock() - tStart) 

,或者在這種特殊情況下,只需使用bytes對象*運營商,這不正是:

bs = MSG*COUNT 

第二反覆函數迭代MSG,存儲數據字節由字節,並具有作爲字節組的生長反覆重新分配存儲器。

您可以通過一次調用替換迭代extend使第二功能幾乎一樣快,原始(未優化)第一招:

def bytearray_test_opt(): 
    tStart = time.clock() 
    ba = bytearray() 
    for i in range(COUNT): 
     ba.extend(MSG) 
    print('array opt time:', time.clock() - tStart) 

此修改後,第二功能會比第一次慢一個只是因爲額外的重新分配(在我的測試中〜15%)。

第三個函數使用bytearray的切片分配,它接受迭代並似乎在做同樣的逐字節迭代,而沒有認識到它們可能只是memcpy字節。這看起來像標準庫中的一個缺陷,可以修復。

從前面的優化中可以看出,與逐字節複製相比,分配佔用的時間非常少,因此預分配在這裏沒有明顯的影響。你可以節省做更少的計算一段時間了,但它不會太大的幫助之一:

def initialized_bytearray_test_opt(): 
    tStart = time.clock() 
    L = len(MSG) 
    ba = bytearray(L*COUNT) 
    ofs = 0 
    for i in range(COUNT): 
     ba[ofs : ofs+L] = MSG 
     ofs += L 
    print('initialized array opt time:', time.clock() - tStart) 

從我的機器的最後時刻:

byte list time: 0.004823000000000001 
byte list opt time: 0.0008649999999999977 
array time: 0.043324 
array opt time: 0.005505999999999997 
initialized array time: 0.05936899999999999 
initialized array opt time: 0.040164000000000005 

附:使用timeit模塊來執行像這樣的措施,它提供了更高的準確性。