Самый быстрый способ упаковать список поплавков в байты в python
У меня есть список флагов типа 100k, и я хочу преобразовать его в буфер байтов.
buf = bytes()
for val in floatList:
buf += struct.pack('f', val)
return buf
Это довольно медленно. Как я могу сделать это быстрее, используя только стандартные библиотеки Python 3.x.
Ответы
Ответ 1
Просто сообщите struct
, сколько float
у вас есть. Поплавки 100k занимают около 1/100 секунды в моем медленном ноутбуке.
import random
import struct
floatlist = [random.random() for _ in range(10**5)]
buf = struct.pack('%sf' % len(floatlist), *floatlist)
Ответ 2
Вы можете использовать ctypes и иметь двойной массив (или массив float) точно так же, как и на 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, упаковка в двойной массив может быть или не быть быстрее, чем вызов struct
, как в принятом ответе Agf - я оставлю измерение, которое быстрее, чем домашнее задание, но весь код вам нужно следующее:
>>> import ctypes
>>> array = (ctypes.c_double * len(floatlist))(*floatlist)
Чтобы увидеть это как строку, просто выполните: str(buffer(array))
- один из недостатков заключается в том, что вам нужно позаботиться о размере float (float vs double) и CPU-float type - структурный модуль может позаботиться об этом для вас.
Большая победа заключается в том, что с помощью массива float вы все равно можете использовать элементы в виде чисел, обращаясь к нему так же, как если бы он имел простой список Python, а затем был легко доступен в виде планарной области памяти с buffer
.
Ответ 3
Как и в случае с строками, использование .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\[email protected]\x00\x00`@\x9a\x99\[email protected]\x9a\x99\[email protected]\[email protected]'
Ответ 4
Это должно работать:
return struct.pack('f' * len(floatList), *floatList)
Ответ 5
Большая часть медленности будет заключаться в том, что вы повторно добавляете к байтовой строке. Это копирует байты каждый раз. Вместо этого вы должны использовать b''.join()
:
import struct
packed = [struct.pack('f', val) for val in floatList]
return b''.join(packed)
Ответ 6
Как вы говорите, что вам действительно нужны поплавки с одной точностью, вы можете попробовать массивный модуль (в стандартная библиотека с 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
Всего: 20 байтов на каждый лучший случай для list
, 4 байта на элемент всегда для array.array('f')
.
Ответ 7
Для массива одинарной точности float существует два варианта: использовать 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
быстрее.