不久前,我用 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 对象进行基准测试?或者,如果您能想出比我的基于存储桶的解决方案更好的方法来“过期”旧数据,那么我会全力以赴。