簡體   English   中英

在python中將浮點數列表打包成字節的最快方法

[英]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

有些人可能可以使用其他性能節省:

  • 您只需再次執行分配即可重用現有緩沖區,而無需創建新緩沖區。
  • 您可以通過分配給適當的切片來修改現有緩沖區的一部分。

對於單精度浮點數組,有兩種選擇:使用structarray

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.

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