Что более эффективно в Python с точки зрения использования памяти и потребления ЦП - словарь или объект?
Предыстория: мне нужно загрузить огромное количество данных в Python. Я создал объект, который представляет собой просто контейнер поля. Создание 4M экземпляров и помещение их в словарь заняло около 10 минут и ~ 6 ГБ памяти. Когда словарь готов, доступ к нему происходит в мгновение ока.
Пример: Чтобы проверить производительность, я написал две простые программы, которые делают то же самое - одна использует объекты, другая словарь:
Объект (время выполнения ~ 18сек):
class Obj(object):
def __init__(self, i):
self.i = i
self.l = []
all = {}
for i in range(1000000):
all[i] = Obj(i)
Словарь (время выполнения ~ 12сек):
all = {}
for i in range(1000000):
o = {}
o['i'] = i
o['l'] = []
all[i] = o
Вопрос: Я что-то делаю не так или словарь просто быстрее, чем объект? Если действительно словарь работает лучше, может кто-нибудь объяснить, почему?
источник
Ответы:
Вы пробовали использовать
__slots__
?Из документации :
Так экономит ли это время и память?
Сравнение трех подходов на моем компьютере:
test_slots.py:
test_obj.py:
test_dict.py:
test_ namedtuple.py (поддерживается в 2.6):
Запустите тест (используя CPython 2.5):
Используя CPython 2.6.2, включая именованный тест кортежа:
Так что да (что не удивительно), использование
__slots__
- это оптимизация производительности. Использование именованного кортежа имеет аналогичную производительность__slots__
.источник
Доступ к атрибутам в объекте использует доступ к словарю за кулисами, поэтому, используя доступ к атрибутам, вы добавляете дополнительные накладные расходы. Кроме того, в случае с объектами вы несете дополнительные накладные расходы, например, из-за дополнительного выделения памяти и выполнения кода (например,
__init__
метода).В вашем коде if
o
являетсяObj
экземпляром,o.attr
что эквивалентноo.__dict__['attr']
небольшим дополнительным накладным расходам.источник
o.__dict__["attr"]
это тот, у которого дополнительные накладные расходы, принимая дополнительный байт-код op; obj.attr быстрее. (Конечно, доступ к атрибутам не будет медленнее, чем доступ по подписке - это критический, сильно оптимизированный путь кода.)Вы рассматривали возможность использования именованного кортежа ? ( ссылка для python 2.4 / 2.5 )
Это новый стандартный способ представления структурированных данных, обеспечивающий производительность кортежа и удобство класса.
Единственным недостатком по сравнению со словарями является то, что (как и кортежи) они не дают вам возможности изменять атрибуты после создания.
источник
Вот копия ответа @hughdbrown для python 3.6.1. Я увеличил счетчик в 5 раз и добавил код для проверки объема памяти, используемого процессом python в конце каждого запуска.
Прежде чем проголосовать против, имейте в виду, что этот метод подсчета размера объектов неточен.
И это мои результаты
Мой вывод:
источник
Полученные результаты:
источник
Нет никаких вопросов.
У вас есть данные без других атрибутов (без методов, ничего). Следовательно, у вас есть контейнер данных (в данном случае словарь).
Я обычно предпочитаю думать в терминах моделирования данных . Если есть какие-то огромные проблемы с производительностью, я могу отказаться от чего-то в абстракции, но только по очень веским причинам.
Программирование - это управление сложностью, и поддержание правильной абстракции очень часто является одним из наиболее полезных способов достижения такого результата.
Что касается причин, по которым объект движется медленнее, я думаю, что ваши измерения неверны.
Вы выполняете слишком мало присваиваний внутри цикла for, и поэтому вы видите разное время, необходимое для создания экземпляра dict (внутреннего объекта) и «настраиваемого» объекта. Хотя с точки зрения языка они одинаковы, но имеют совершенно разную реализацию.
После этого время назначения должно быть почти одинаковым для обоих, поскольку в конечном итоге члены сохраняются внутри словаря.
источник
Есть еще один способ уменьшить использование памяти, если структура данных не должна содержать ссылочные циклы.
Сравним два класса:
и
Это стало возможным, поскольку
structclass
классы -base не поддерживают циклическую сборку мусора, которая в таких случаях не нужна.Есть также одно преимущество над
__slots__
классом -based: вы можете добавлять дополнительные атрибуты:источник
Вот мои тестовые прогоны очень красивого сценария @ Jarrod-Chesney. Для сравнения я также запустил его против python2, заменив «range» на «xrange».
Из любопытства я также добавил похожие тесты с OrderedDict (ordict) для сравнения.
Python 3.6.9:
Python 2.7.15+:
Итак, по обеим основным версиям выводы @ Jarrod-Chesney по-прежнему выглядят хорошо.
источник