Как получить размер словаря Numba? - PullRequest
2 голосов
/ 08 января 2020

Я пытаюсь получить размер набранного словаря Numba в байтах:

from numba import njit
from numba.typed import Dict
from sys import getsizeof as gso
@njit
def get_dict(n):
    d = {0:0}
    for i in range(1,n):
        d[i] = i
    return d

print(gso(get_dict(10))
print(gso(get_dict(10000)))

В обоих случаях функция getsizeof возвращает 64 байт. Очевидно, что размер словаря должен зависеть от длины.

Если я преобразую типизированный словарь в собственный Python словарь, используя dict () , он работает и возвращает 376 и 295016:

print(gso(dict(get_dict(10))))
print(gso(dict(get_dict(10000))))

Как я могу это измерить?

1 Ответ

2 голосов
/ 08 января 2020

В настоящее время (numba 0.46.0) то, что вы намеревались сделать, скорее всего, невозможно с DictType.

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

Первая проблема заключается в том, что вызов sys.getsizeof для контейнера обычно сообщает только о размере контейнера, а не о содержимом контейнер - или в случае непрозрачного контейнера он возвращает только размер оболочки. Вы столкнулись с последним: DictType - это просто оболочка для непрозрачной структуры numba, которая определена (вероятно) в . Таким образом, 64, который вы видите, на самом деле правильный, это размер оболочки. Возможно, вы можете получить доступ к упакованному типу, но, учитывая, что он скрыт за закрытыми атрибутами, он не предназначен для не-numba-кода, поэтому я не буду go идти по этому пути - в основном потому, что любой ответ, основанный на таких деталях реализации, может быть устаревшим в любое время.

Однако sys.getsizeof требует глубокого понимания деталей реализации для правильной интерпретации. Так что даже для простого dict не очевидно, что представляет число. Это, конечно, вычисленная память в байтах контейнера (без содержимого), но это также может быть словарь совместного использования ключей (в вашем случае это не словарь совместного использования ключей), где число будет точным, но так как часть ключа совместное использование словаря - это не тот номер, который вы ищете. Как уже упоминалось, обычно он также не учитывает содержимое контейнера, но это также деталь реализации, например, numpy.array включает размер содержимого, тогда как list, set, et c. нет. Это связано с тем, что массив numpy не имеет «реального содержимого» - по крайней мере он не содержит Python объектов в качестве содержимого.

Так что даже если оболочка DictType сообщит о размере базовый словарь, вам все равно нужно знать, содержит ли он содержимое в словаре, или как указатель на Python объекты или (даже более запутанный) как объекты, определенные в другом языке (например, C или C ++) для интерпретации результаты правильные.

Поэтому я бы посоветовал вам , а не , использовать sys.getsizeof, кроме как из любопытства или интереса c. И только тогда, если вы готовы копаться во всех деталях реализации (которые могут меняться или не меняться в любое время), чтобы правильно интерпретировать результаты. Если вы действительно заинтересованы в потреблении памяти, вам часто лучше использовать инструмент, который отслеживает использование памяти вашей программой в целом. Это все еще имеет много подводных камней (повторное использование памяти, неиспользуемые выделения памяти) и требует значительного объема знаний о том, как память используется для правильной интерпретации (виртуальная память, разделяемая память и как выделяется память), но часто дает более realisti c посмотреть, сколько памяти фактически использует ваша программа.

import gc
import numba as nb
import psutil

@nb.njit
def get_dict(n):
    d = {0:0}
    for i in range(1,n):
        d[i] = i
    return d

get_dict(1)

gc.collect()
print(psutil.Process().memory_info())
d = get_dict(100_000)
gc.collect()
print(psutil.Process().memory_info())

Что дает на моем компьютере:

pmem(rss=120696832, vms=100913152, num_page_faults=34254, peak_wset=120700928,
     wset=120696832, peak_paged_pool=724280, paged_pool=724280, peak_nonpaged_pool=1255376,
     nonpaged_pool=110224, pagefile=100913152, peak_pagefile=100913152, private=100913152)

pmem(rss=126820352, vms=107073536, num_page_faults=36717, peak_wset=129449984, 
     wset=126820352, peak_paged_pool=724280, paged_pool=724280, peak_nonpaged_pool=1255376, 
     nonpaged_pool=110216, pagefile=107073536, peak_pagefile=109703168, private=107073536)

Что показывает, что программе требовалось 6 123 520 байт памяти ( использование "rss" - размер резидентного набора) больше после вызова, чем было выделено ранее.

И аналогично для простого Python словаря:

import gc
import psutil

gc.collect()
print(psutil.Process().memory_info())
d = {i: i for i in range(100_000)}
gc.collect()
print(psutil.Process().memory_info())
del d
gc.collect()

Что дало разницу в 8 552 448 байтов на моем компьютере.

Обратите внимание, что эти числа представляют собой полный процесс, поэтому относитесь к ним с осторожностью. Например, для небольших значений (get_dict(10)) они возвращают 4096 на моем windows компьютере, потому что это размер страницы windows. Программа фактически выделяет больше места, чем нужно словарю из-за ограничений ОС.

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

Если вы все еще (из любопытства) хотите узнать, сколько памяти теоретически нужно DictType, вам, вероятно, следует попросить разработчиков numba улучшить numba, чтобы они внедрили __sizeof__ для своих оболочек Python, чтобы числа являются более представительными. Вы можете, например, открыть проблему на их трекере или задать в их списке рассылки .

Альтернативой может быть использование других сторонних инструментов, например pypmler, но я сам ими не пользовался, поэтому не знаю, работают ли они в этом случае.

...