我正在尝试在cython中实现一种算法。但是我的BitSet
实现中的内存不足。我不知道为什么,这是我的代码的有用快照。
bitset.pyx
# distutils: language = c++
from libcpp.vector cimport vector
cdef class BitSet:
def __cinit__(self, int size):
self.vector = vector[bint](size)
cpdef void inter(self, BitSet other) except *:
# Do the intersection in place between two bitset
cpdef void add(self, int element):
if 0 <= element < self.vector.size():
self.vector[element] = True
bitset.pxd
# distutils: language = c++
from libcpp.vector cimport vector
cdef class BitSet:
cdef public vector[bint] vector
cpdef void inter(self, BitSet other) except *
cpdef void add(self, int element)
我需要创建一个list
的Python BitSet
(大约12_000),每个大小为1_000_000。我想这应该占用1_000_000位(以存储Bint类型)* 12000 = 1.5 GB
但是我的内存快要用完了,这里有一张图片来解释更多
曲线的最低点为1.5 GB,最高点为7 GB。
我用一个庞大的列表列表来调用该程序,这可能解释了第一个高峰,第二个高峰可能是我所面临的那个峰。
只有100_000个序列后,我的程序内存不足。
这是我的主要内容:
cdef class Main:
def __cinit__(self):
self.number_sequences # Int
self.foo = [] # python list type
def train(self, sequences):
self.number_sequences = len(sequences)
for id_seq, sequence in enumerate(sequences):
for element in sequence:
while not element < len(self.foo):
self.foo.append(BitSet(self.number_sequences))
self.foo[element].add(id_seq)
我对内存使用的估计是否错误?为什么呢?
如何追踪我的记忆?我找不到用于cython的任何工具。
有什么解决方案可以使其适合内存? (用python整数集替换BitSet可以,但是它要慢得多,并且应该占用更多空间)
答案 0 :(得分:1)
bint
只是一个方便的整数类型,可用于存储true / false值。与所有其他C类型一样,它需要具有一个可以以整个字节为单位进行度量的地址,因此它至少占用1个字节(实际上,它看起来更多)。
为了以节省空间的方式存储布尔值,您需要存储整数类型,然后进行一些操作以访问单个元素:
cdef uint8_t x = some_value
nth_element_is_true = bool(x & (1<<n)) # bitshift to get a suitable mask then bitwise and
您显然可以将其扩展为使用数组来存储多个元素。
存在一些明显的预制可能性:
首先,您可以使用numpy.packbits
和unpackbits
。不过,这确实会产生相当昂贵的临时变量(例如unpackbits
将创建一个大小为8倍的数组)。
第二,您可以使用std::vector<bool>
,它已经过优化,每个元素使用1位:
from libcpp.vector cimport vector
from libcpp cimport bool
cdef vector[bool] vb = vector[bool](1000000)
创建专门的vector<bool>
的行为与正常vector
的行为不同,在C ++中,这是一个不好的主意,但是它可以做什么你想要的。