Самый быстрый способ упаковать список поплавков в байты в python - программирование
Подтвердить что ты не робот

Самый быстрый способ упаковать список поплавков в байты в python

У меня есть список флагов типа 100k, и я хочу преобразовать его в буфер байтов.

buf = bytes()
for val in floatList:
   buf += struct.pack('f', val)
return buf

Это довольно медленно. Как я могу сделать это быстрее, используя только стандартные библиотеки Python 3.x.

4b9b3361

Ответ 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 быстрее.