Производительность Python и алгоритмов сжатия - PullRequest
0 голосов
/ 20 декабря 2018

Меня интересует этот алгоритм сжатия (проверьте ссылку)

https://github.com/bright-tools/varints

В частности, проблема состоит в том, что накладные расходы памяти для объектов bytearray в Python делают сжатие бесполезным.Есть решение, которое учитывает только размер кодирования, а НЕ размер структуры данных?Например:

>>> import sys
>>> list = []
>>> sys.getsizeof(list)
    64

Но у меня было бы что-то вроде "0", а не 64

Как я могу избежать накладных расходов памяти?Некоторые приходят, пожалуйста?

1 Ответ

0 голосов
/ 21 декабря 2018

Python - это не тот язык, который вы хотите использовать, если вы пытаетесь создать крошечные структуры данных.В качестве README для проекта, с которым вы связываете заметки, вы можете использовать байтовые массивы (не списки), чтобы уменьшить накладные расходы на хранение, если вы можете упаковать много данных в один байтовый массив.Но вам приходится иметь дело с тем фактом, что байтовые массивы неизменны;Вы не можете делать такие вещи, как добавление другого элемента или изменение существующего без создания нового массива байтов.И даже байтовые массивы (например, строки) обходятся дорого: установка 64-битного CPython, то есть стандартный Python, который вы получите при установке Linux на x86, использует не менее 33 байтов для каждого байтового массива.(Я говорю «по крайней мере», потому что у Python нет способа измерить накладные расходы на выделение памяти.) И затем есть вычислительные затраты на десериализацию потока байтов в исходные объекты, если вам нужно использовать их для чего-то отличного от ключа хеш-таблицы.

Поскольку связанная страница создает объекты меньшего размера, я пришел к выводу, что ее тесты должны были проводиться при установке 32-битного Python, возможно, в Windows.Так что это один из способов сократить использование хранилища.

Если у вас есть Python3.3 или новее (а если нет, просто установите его :-)), то вы можете использовать arrayмодуль, который, вероятно, будет намного удобнее, чем байтовый массив, отчасти потому, что вы можете создать массив нужного вам размера.Вы также можете изменить элементы в массиве, которые могут быть полезны или не полезны.См. руководство по Python для подробностей.Если вы строите array.array с использованием модификаторов типа b или B, он будет использовать только один байт на значение.Если вы используете h или H, вы можете хранить 16-битные целые числа (со знаком или без знака), каждое в двух байтах.Накладные расходы array.array составляют 64 байта, как и список, но фактические элементы намного более компактны.

Лично я бы не стал беспокоиться о таких вещах, но я полагаю, что он имеет свои применения,Фактически, ссылочная страница README недооценивает потребление памяти списком целых чисел в Python, поскольку она не учитывает размер самих целых чисел, что является значительным.

Размер списка, раскрываемыйsys.getsizeof - это только размер самого списка.Он не включает объекты в списке, только ссылки на объект (по восемь байт в стандартной установке Linux Python).Он также включает в себя память, используемую описанием объекта списка, которая составляет 64 байта при той же стандартной установке Python.(Это 64 байта, показанные в вашем тесте.)

Наконец, он может включать некоторое дополнительное пространство в конце, чтобы разрешить добавление элементов в список без необходимости перераспределения и копированиясписок.Количество таких дополнительных объектов зависит от множества факторов, в том числе от точного способа составления списка, но, похоже, вы можете уменьшить эти конкретные накладные расходы до нуля, скопировав список со срезом: a[:].

В Python целые числа являются полноценными объектами, и они занимают удивительное количество места.Или, может быть, это не так удивительно, если учесть, что целые числа Python являются бигнумами, поэтому у них нет искусственного ограничения размера.Согласно getsizeof целые числа, абсолютное значение которых меньше 2 30 , занимают 28 байтов, а каждые дополнительные 30 бит (или часть) стоят еще четыре байта.(На самом деле, вы можете разбить большой вектор маленьких целых чисел в один бигнум, воспользовавшись тем, что операции левого и правого сдвига достаточно быстрые, и тем самым сбрасывают еще несколько байтов из каждого списка. Ноarray.array почти наверняка проще.)


Некоторые эксперименты с getsizeof, для справки:

>>> from sys import getsizeof
>>> # Strings occupy 48 bytes plus the length of the string plus one byte (presumably for a NUL)
>>> getsizeof("")   # 48 + 0 + 1
49
>>> getsizeof("a")  # 48 + 1 + 1
50
>>> getsizeof("abcdefghijklmnopqrstuvwxyz") # 48 + 26 + 1
75
>>> getsizeof("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") # 48 + 52 + 1
101
>>> But that's not counted in the size of a list. All the lists are the same size:
>>> getsizeof([""])
72
>>> getsizeof(["a"])
72
>>> getsizeof(["abcdefghijklmnopqrstuvwxyz"])
72
>>> getsizeof(["abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"])
72
>>> # Same for a list containing a single number
>>> getsizeof([0])
72
>>> # Lists need 64 bytes plus 8 bytes per element (a pointer to the element):
>>> getsizeof([0,1])
80
>>> getsizeof([0,1,2])
88
>>> getsizeof([0,1,2,3])
96
>>> # When you append to a list, Python leaves some extra space for the next appends
>>> a = [0,1,2,3]
>>> getsizeof(a)
96
>>> # As above, 64 + 4 * 8 bytes. But when we add a single element,
>>> # we get enough room for four elements, so the next three appends
>>> # don't require more space:
>>> a.append(4)
>>> getsizeof(a)
128                 
>>> a.append(5)
>>> getsizeof(a)
128
>>> a.append(6)
>>> getsizeof(a)
128
>>> a.append(7)
>>> getsizeof(a)
128
>>> # When we append the 9th element, we get room for another four
>>> a.append(8)
>>> getsizeof(a)
192

Вы можете сохранить несколько байтов, используя кортежи вместо списков:кортеж, как байтовый массив, является неизменным, но если вы можете жить, не имея возможности изменить объект, вы можете сохранить 16 байтов, используя кортеж вместо списка:

>>> getsizeof( (1,2,3) )
72
>>> getsizeof( [1,2,3] )
88
...