Который занимает меньше памяти, фрозенсет или кортеж? - PullRequest
2 голосов
/ 07 июля 2019

У меня есть объект, который нужно «пометить» 0-3 строками (из набора из 20 возможных вариантов);все эти значения уникальны, и порядок не имеет значения.Единственная операция, которая должна быть сделана над тегами, - это проверка наличия или отсутствия конкретного (specific_value in self.tags).

Однако, в памяти одновременно находится огромное количество этих объектов.что это расширяет границы ОЗУ моего старого компьютера.Таким образом, можно сэкономить несколько байтов.

С таким небольшим количеством тегов для каждого объекта, я сомневаюсь, что время поиска будет иметь большое значение.Но есть ли разница в памяти между использованием кортежа и фрозенсета?Есть ли какая-либо другая реальная причина использовать один над другим?

Ответы [ 3 ]

4 голосов
/ 07 июля 2019

sys.getsizeof похоже на вариант stdlib, который вы хотите ... но я чувствую тошноту по поводу всего вашего варианта использования

import sys
t = ("foo", "bar", "baz")
f = frozenset(("foo","bar","baz"))
print(sys.getsizeof(t))
print(sys.getsizeof(f))

https://docs.python.org/3.7/library/sys.html#sys.getsizeof

Все построено-in объекты будут возвращать правильные результаты, но это не должно выполняться для сторонних расширений, поскольку это зависит от реализации.

... Так что не соглашайтесь с этим решением

РЕДАКТИРОВАТЬ: Очевидно, ответ @TimPeters является более правильным ...

3 голосов
/ 07 июля 2019

Кортежи очень компактны.Наборы основаны на хеш-таблицах и зависят от наличия «пустых» слотов, чтобы снизить вероятность коллизий хеш-функций.

Для достаточно недавней версии CPython, sys._debugmallocstats() отображает много потенциально интересной информации.Здесь в 64-битном Python 3.7.3:

>>> from sys import _debugmallocstats as d
>>> tups = [tuple("abc") for i in range(1000000)]

tuple("abc") создает кортеж из 3 1-символьных строк, ('a', 'b', 'c').Вот я отредактирую почти все выходные данные:

>>> d()
Small block threshold = 512, in 64 size classes.

class   size   num pools   blocks in use  avail blocks
-----   ----   ---------   -------------  ------------
...
    8     72       17941         1004692             4

Так как мы создали миллион кортежей, очень хорошо, что класс размера, использующий блоки 1004692, - тот, который мы хотим ;-) Каждый изблоки потребляют 72 байта.

Вместо этого, вместо этого, вывод показывает, что они потребляют 224 байта каждый, чуть более чем в 3 раза:

>>> tups = [frozenset(t) for t in tups]
>>> d()
Small block threshold = 512, in 64 size classes.

class   size   num pools   blocks in use  avail blocks
-----   ----   ---------   -------------  ------------
...
   27    224       55561         1000092             6

В этом конкретном случае другой ответ вамслучайность дает те же результаты:

>>> import sys
>>> sys.getsizeof(tuple("abc"))
72
>>> sys.getsizeof(frozenset(tuple("abc")))
224

Хотя это часто и так, но это не всегда так, потому что объект может потребовать выделения байт больше, чем на самом деле, чтобы удовлетворить выравнивание HWтребования.getsizeof() ничего об этом не знает, но _debugmallocstats() показывает количество байтов, которое фактически должен использовать распределитель малых объектов Python.

Например,

>>> sys.getsizeof("a")
50

На32-битный блок, 52 байта фактически необходимо использовать, чтобы обеспечить 4-байтовое выравнивание.В 64-битном боксе в настоящее время требуется 8-байтовое выравнивание, поэтому необходимо использовать 56 байтов.В Python 3.8 (еще не выпущен) для 64-битного блока требуется 16-байтовое выравнивание, и необходимо использовать 64 байта.

Но, игнорируя все это, кортежу всегда потребуется меньше памяти, чемлюбая форма множества с одинаковым количеством элементов - и даже меньше, чем список с таким же количеством элементов.

2 голосов
/ 07 июля 2019

Если вы пытаетесь сэкономить память, рассмотрите

  • Отменяйте некоторую элегантность для некоторой экономии памяти, извлекая структуру данных, из которых представлены теги, во внешнюю (одноэлементную) структуру данных
  • Использование подхода типа «флаги» (битовая карта), где каждый тег отображается в бит 32-битного целого числа.Тогда все, что вам нужно, это (singleton) dict отображение из объекта (удостоверения) в 32-разрядное целое число (флаги).Если нет никаких флагов, нет записи в словаре.
...