2

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

我必须保持精简的数据通常是一个将字符串映射到字符串列表的字典。更具体地说,消息中的每个单词都映射到所有可能的后续单词。这仍然过于简单化,但足以将我的问题置于上下文中。

目前,我正在努力解决的解决方案涉及管理数据“桶”。它将跟踪每个存储桶的外观大小,一旦达到一定大小就“归档”一个存储桶并转移到一个新的存储桶,在大约 5 个存储桶之后,它会在每次新存储桶时删除最旧的“归档”存储桶创建的。这具有简单的优点:删除整个存储桶不会产生任何死胡同或无法到达的单词,因为来自每条消息的单词都进入同一个存储桶。

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

我第一次尝试使用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

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

简而言之:有没有办法准确地对单个 Python 对象进行基准测试?或者,如果您能想出比我的基于存储桶的解决方案更好的方法来“过期”旧数据,那么我会全力以赴。

4

1 回答 1

1

这可能是一个 hacky 解决方案,但如果您的存储桶对象是可腌制的(听起来像是),您可以腌制它们并测量腌制对象字符串的字节长度。它可能不完全是内存中解压缩对象的大小,但它应该随着对象的增长而线性增长,并让您对对象之间的相对大小有一个相当好的了解。

为了避免不得不腌制非常大的对象,您可以通过单独腌制来测量添加到存储桶中的每个条目的大小,并将其字节长度添加到存储桶的总字节长度属性中。但是请记住,如果您这样做,条目和存储桶的内部绑定中将使用一些开销内存,这些内存不会反映在条目本身的独立大小上,但是您可以运行一些测试来分析这并找出超出其实际大小的每个新条目的 %memory 开销。

于 2013-02-11T03:42:22.407 回答