たとえば100kの浮動小数点数のリストがあり、それをバイトバッファに変換したい。
buf = bytes()
for val in floatList:
buf += struct.pack('f', val)
return buf
これは非常に遅いです。標準のPython 3.xライブラリのみを使用して高速化するにはどうすればよいですか。
struct
の数float
sを教えてください。私の遅いラップトップでは、100kのフロートは約1/100秒かかります。
import random
import struct
floatlist = [random.random() for _ in range(10**5)]
buf = struct.pack('%sf' % len(floatlist), *floatlist)
Ctypesを使用して、データをリストに保持する代わりに、Cの場合とまったく同じようにdouble配列(またはfloat配列)を使用できます。これはかなり低いレベルですが、優れたパフォーマンスが必要で、リストのサイズが固定されている場合に推奨されます。
PythonでC double array[100];
に相当するものを作成できます。
array = (ctypes.c_double * 100)()
ctypes.c_double * 100
式は、Python double、100項目の長さの配列のクラスです。ファイルに配線するには、buffer
を使用してその内容を取得できます。
>>> f = open("bla.dat", "wb")
>>> f.write(buffer(array))
あなたのデータがすでにPythonリストにある場合、ダブル配列にパックすることは、Agfの受け入れられた答えでstruct
asを呼び出すよりも速いかもしれません。宿題のように速くなりますが、必要なコードはすべてこれです:
>>> import ctypes
>>> array = (ctypes.c_double * len(floatlist))(*floatlist)
str(buffer(array))
-ここでの1つの欠点は、floatサイズ(float vs double)とCPU依存のfloat型を処理する必要があることです-structモジュールが処理できますこれはあなたのため。
大きな利点は、フロート配列を使用すると、プレーンPythonリストでありながら平面メモリ領域としてすぐに利用できるようにアクセスすることで、要素を数値として使用できることです。 buffer
で。
いくつかの答えが示唆しています
import struct
buf = struct.pack(f'{len(floatlist)}f', *floatlist)
しかし、 '*
'を使用すると、struct.pack
に渡す前にfloatlist
をTupleに不必要に変換します。これは、最初に空のバッファを作成し、次に、私が見つけた最速の方法:スライス割り当てを使用してデータを入力することにより、回避できます。
import ctypes
buf = (ctypes.c_double * len(floatlist))()
buf[:] = floatlist
一部の人々が使用できるその他のパフォーマンスの節約:
単精度浮動小数点数の配列には、struct
またはarray
を使用する2つのオプションがあります。
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 module (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 floatオブジェクト(4または8バイト)プラスそのオブジェクトのサイズへのポインターを消費します。32ビットCPython実装では16です。
_>>> import sys
>>> sys.getsizeof(123.456)
16
_
合計:list
の場合はアイテムごとに20バイト、array.array('f')
の場合は常にアイテムごとに4バイト。