[英]Fastest way to pack a list of floats into bytes in python
我有一個 100k 浮點數的列表,我想將其轉換為字節緩沖區。
buf = bytes()
for val in floatList:
buf += struct.pack('f', val)
return buf
這是相當緩慢的。 如何僅使用標准 Python 3.x 庫使其更快。
只需告訴struct
你有多少個float
。 在我的慢速筆記本電腦上,100k 浮點數大約需要 1/100 秒。
import random
import struct
floatlist = [random.random() for _ in range(10**5)]
buf = struct.pack('%sf' % len(floatlist), *floatlist)
您可以使用 ctypes,並像在 C 中一樣使用雙數組(或浮點數組),而不是將數據保存在列表中。 這是相當低的級別,但如果您需要出色的性能並且您的列表大小固定,這是一個建議。
您可以創建等效的 C double array[100];
在 Python 中執行以下操作:
array = (ctypes.c_double * 100)()
ctypes.c_double * 100
表達式為雙精度數組生成一個 Python 類,長度為 100 項。 要將其連接到文件,您只需使用buffer
即可獲取其內容:
>>> f = open("bla.dat", "wb")
>>> f.write(buffer(array))
如果您的數據已經在 Python 列表中,那么將其打包到雙數組中可能比在 Agf 接受的答案中調用struct
快,也可能不快 - 我將測量哪個更快作為作業,但您需要的所有代碼是:
>>> import ctypes
>>> array = (ctypes.c_double * len(floatlist))(*floatlist)
要將其視為字符串,只需執行以下操作: str(buffer(array))
- 這里的一個缺點是您必須處理浮點大小(浮點與雙精度)和依賴於 CPU 的浮點類型 - struct 模塊可以處理這給你。
最大的好處是,使用浮點數組,您仍然可以將元素用作數字,就像訪問普通 Python 列表一樣訪問 then ,同時可以隨時用作帶有buffer
的平面內存區域。
幾個答案建議
import struct
buf = struct.pack(f'{len(floatlist)}f', *floatlist)
但是在將它傳遞給struct.pack
之前,使用 ' *
' 不必要地將floatlist
轉換為元組。 避免這種情況會更快,首先創建一個空緩沖區,然后使用切片分配填充它:
import ctypes
buf = (ctypes.c_double * len(floatlist))()
buf[:] = floatlist
有些人可能可以使用其他性能節省:
對於單精度浮點數組,有兩種選擇:使用struct
或array
。
In[103]: import random
import struct
from array import array
floatlist = [random.random() for _ in range(10**5)]
In[104]: %timeit struct.pack('%sf' % len(floatlist), *floatlist)
100 loops, best of 3: 2.86 ms per loop
In[105]: %timeit array('f', floatlist).tostring()
100 loops, best of 3: 4.11 ms per loop
所以struct
更快。
那應該工作:
return struct.pack('f' * len(floatList), *floatList)
與字符串一樣,使用.join()
將比連續連接更快。 例如:
import struct
b = bytes()
floatList = [5.4, 3.5, 7.3, 6.8, 4.6]
b = b.join((struct.pack('f', val) for val in floatList))
結果是:
b'\xcd\xcc\xac@\x00\x00`@\x9a\x99\xe9@\x9a\x99\xd9@33\x93@'
正如您所說,您確實想要單精度“f”浮點數,您可能想嘗試使用array 模塊(在 1.x 之后的標准庫中)。
>>> mylist = []
>>> import array
>>> myarray = array.array('f')
>>> for guff in [123.45, -987.654, 1.23e-20]:
... mylist.append(guff)
... myarray.append(guff)
...
>>> mylist
[123.45, -987.654, 1.23e-20]
>>> myarray
array('f', [123.44999694824219, -987.6539916992188, 1.2299999609665927e-20])
>>> import struct
>>> mylistb = struct.pack(str(len(mylist)) + 'f', *mylist)
>>> myarrayb = myarray.tobytes()
>>> myarrayb == mylistb
True
>>> myarrayb
b'f\xe6\xf6B\xdb\xe9v\xc4&Wh\x1e'
這可以為您節省大量內存,同時仍然具有包含大多數列表方法的可變長度容器。 array.array 方法每個單精度浮點數占用 4 個字節。 列表方法使用一個指向 Python 浮點對象(4 或 8 個字節)加上該對象大小的指針; 在 32 位 CPython 實現上,即 16:
>>> import sys
>>> sys.getsizeof(123.456)
16
總計:對於list
,每項最佳情況為 20 個字節,對於array.array('f')
, array.array('f')
始終為 4 個字節。
在我看來,最好的方法是創建一個循環:
例如
import struct
file_i="test.txt"
fd_out= open ("test_bin_file",'wb')
b = bytes()
f_i = open(file_i, 'r')
for riga in file(file_i):
line = riga
print i,float(line)
i+=1
b=struct.pack('f',float(line))
fd_out.write(b)
fd_out.flush()
fd_out.close()
要附加到現有文件,請改用:
fd_out= open ("test_bin_file",'ab')
大多數緩慢將是您反復附加到字節串。 每次都復制字節串。 相反,您應該使用b''.join()
:
import struct
packed = [struct.pack('f', val) for val in floatList]
return b''.join(packed)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.