当旧数据消耗太多内存时,如何实现清除旧数据的马尔可夫链?

2024-10-03 21:30:16 发布

您现在位置:Python中文网/ 问答频道 /正文

不久前,我用Python为IRC编写了一个Markov chain文本生成器。在运行一两个月后,它会消耗掉我VPS的所有空闲内存,我需要清除它的数据并重新开始。现在我正在重写它,我想尽可能优雅地处理内存问题。在

我需要保留的数据通常是一个将字符串映射到字符串列表的字典。更具体地说,消息中的每个单词都映射到所有可能的后续单词。这仍然是一个过于简单化,但它已经足够将我的问题具体化。在

目前,我正在努力解决的解决方案是管理“桶”数据。它会跟踪每个bucket的表观大小,当bucket达到一定的大小时,“archive”一个bucket并转移到一个新的bucket,大约5个bucket之后,它会在每次创建新bucket时删除最旧的“archived”bucket。这有一个简单的优点:删除一个完整的bucket不会创建任何死胡同或无法访问的单词,因为每个消息中的单词都进入同一个bucket中。在

问题是“跟踪每个桶的表观尺寸”说起来容易做起来难。在

我第一次尝试使用sys.getsizeof,但很快发现,确定对象在内存中的实际大小是不切实际的。我还研究了guppy/heapy/各种其他内存使用模块,但它们似乎都没有达到我所期望的效果(即,对单个对象进行基准测试)。目前我正在试验较低级别的psutil模块。以下是应用程序当前状态的摘录:

class Markov(object):
    # (constants declared here)
    def __init__(self):
        self.proc = psutil.Process(os.getpid())
        self.buckets = []
        self._newbucket()

    def _newbucket(self):
        self.buckets.append(copy.deepcopy(self.EMPTY_BUCKET))

    def _checkmemory(f):
        def checkmemory(self):
            # Check memory usage of the process and the entire system
            if (self.proc.get_memory_percent() > self.MAX_MEMORY
                    or psutil.virtual_memory().percent > self.MAX_TOTAL_MEMORY):
                self.buckets.pop(0)
            # If we just removed the last bucket, add a new one
            if not self.buckets:
                self._newbucket()
            return f()
        return checkmemory

    @_checkmemory
    def process(self, msg):
        # generally, this adds the words in msg to self.buckets[-1]

    @_checkmemory
    def generate(self, keywords):
        # generally, this uses the words in all the buckets to create a sentence

这里的问题是,这只会使bucket过期;我不知道何时“归档”当前的bucket,因为Python的开销内存使我无法准确地确定距离到达self.MAX_MEMORY有多远。更不用说,Markov类实际上是由无头IRC客户机管理的许多“插件”之一(为了简洁起见,我省略了另一个细节),因此开销不仅存在,而且不可预测。在

简而言之:有没有一种方法可以精确地对单个Python对象进行基准测试?或者,如果您能想出比我的基于桶的解决方案更好的“过期”旧数据的方法,我会洗耳恭听。在


Tags: the数据对象内存selfbucketdef单词
1条回答
网友
1楼 · 发布于 2024-10-03 21:30:16

这可能是一个有点老套的解决方案,但是如果bucket对象是可pickle的(听起来像是这样),那么可以对它们进行pickle并测量pickled对象字符串的字节长度。它可能不是内存中解压对象的大小,但它应该随着对象的增长而线性增长,并使您对对象之间的相对大小有一个相当好的了解。在

为了避免必须对非常大的对象进行pickle,您可以测量添加到bucket中的每个条目的大小,方法是单独对其进行酸洗,并将其bytelength添加到bucket的total bytelength属性中。 不过,请记住,如果这样做,条目和bucket的内部绑定中将使用一些开销内存,这些内存不会由条目本身的独立大小反映出来,但是您可以运行一些测试来分析这一点,并计算出超出实际大小的每个新条目的内存开销百分比。在

相关问题 更多 >