如何获取 Numba 词典的大小?

How to get size of a Numba Dictionary?

我正在尝试以字节为单位获取 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 函数 returns 64 字节。显然,字典的大小必须取决于长度。

如果我使用 dict() 将类型化字典转换为本机 Python 字典,它会起作用并且 returns 376 和 295016:

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

如何测量?

目前 (numba 0.46.0) DictType.

您打算做的事情很可能无法实现

sys.getsizeof 在容器上 充其量是棘手的,在最坏的情况下非常具有误导性。问题是 getsizeof 需要将可能非常复杂的实现细节减少到一个整数。

第一个问题是在容器上调用 sys.getsizeof 通常只报告容器的大小而不是容器的内容 - 或者在不透明容器的情况下它只 return包装纸的大小。您遇到的是后者 - DictType 只是一个不透明的 numba 结构的包装器,该结构(可能)在 中定义。所以您看到的 64 实际上是正确的,这就是包装器的大小。您可能可以访问包装类型,但鉴于它隐藏在私有属性后面,它不适用于非 numba 代码,所以我不会走那条路——主要是因为任何依赖于这些实现细节的答案都可能已经过时随时。

但是 sys.getsizeof 需要深入理解实现细节才能正确解释。因此,即使对于普通的 dict ,数字所代表的含义也不明显。它当然是以字节为单位计算的容器内存(没有内容),但它也可能是一个密钥共享字典(在你的情况下它不是一个密钥共享字典),其中数字是准确的,但由于密钥的一部分 - sharing dictionary are shared 它可能不是你要找的号码。如前所述,它通常也不考虑容器的内容,但这也是一个实现细节,例如 numpy.array 包括内容的大小,而 listset等不要。那是因为 numpy 数组没有 "real contents" - 至少它没有 Python 对象作为内容。

所以即使 DictType 包装器会报告底层字典的大小,您仍然需要知道它是否包含字典中的内容,或者作为指向 Python 对象的指针或(甚至更复杂)作为用另一种语言(如 C 或 C++)定义的对象来正确解释结果。

所以我的建议是不要使用sys.getsizeof,除非出于好奇或学术兴趣。然后只有当您愿意深入了解所有实施细节(可能会或可能不会随时更改)以正确解释结果时。如果您真的对内存消耗感兴趣,通常最好使用一个工具来跟踪整个程序的内存使用情况。这仍然有很多陷阱(内存重用、未使用的内存分配)并且需要大量的知识如何使用内存才能正确解释(虚拟内存、共享内存以及内存分配方式),但它通常会产生更多实际查看您的程序实际使用了多少内存。

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)),它们在我的 windows 计算机上 return 4096 因为那是 windows 的页面大小。由于 OS 限制,那里的程序实际上分配的 space 比字典需要的多。

然而,即使有这些陷阱和限制,如果您对程序的内存要求感兴趣,仍然会更加准确。

如果您仍然(出于好奇)想知道 DictType 理论上需要多少内存,您可能应该要求 numba 开发人员增强 numba,以便他们为他们的 Python 包装纸,使数字更具代表性。例如,您可以 open an issue on their issue tracker or ask on their mailing list.

一个替代方案可能是使用其他第三方工具,例如 pypmler,但我自己没有使用过这些工具,所以我不知道它们是否适用于这种情况。