2

しばらく前に、Python で IRC 用のマルコフ連鎖テキスト ジェネレーターを作成しました。1、2 か月実行すると、VPS の空きメモリがすべて消費され、データを消去して最初からやり直す必要がありました。今、私はそれを書き直しており、できるだけエレガントにメモリの問題に取り組みたいと思っています。

切り捨てておく必要があるデータは、通常、文字列を文字列のリストにマップする辞書です。より具体的には、メッセージ内の各単語は、可能なすべての後続の単語にマップされます。これはまだ単純化しすぎていますが、問題を文脈化するには十分です。

現在、私が取り組んでいるソリューションには、データの「バケット」の管理が含まれます。各バケットの見かけのサイズを追跡し、特定のサイズに達するとバケットを「アーカイブ」して新しいバケットに移動します。バケットが 5 つほど経過すると、新しいバケットが作成されるたびに最も古い「アーカイブ済み」バケットが削除されます。作成した。これには、単純さという利点があります。バケット全体を削除しても、各メッセージの単語がすべて同じバケットに移動するため、行き止まりや到達不能な単語が作成されることはありません。

問題は、「各バケットの見かけのサイズを追跡する」ことは、口で言うほど簡単ではないということです。

最初に を使用してみましたが、メモリ内のオブジェクトの実際のsys.getsizeofサイズを決定するのに実用的ではないことがすぐにわかりました。グッピー/ヒーピー/その他のさまざまなメモリ使用モジュールも調べましたが、探していることを実行しているようには見えません(つまり、単一のオブジェクトのベンチマーク)。現在、下位レベルの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 クライアントによって管理される多くの「プラグイン」の 1 つです (簡潔にするために省略した別の詳細)。そのため、オーバーヘッドは存在するだけでなく、予測不可能です。

要するに、単一の Python オブジェクトを正確にベンチマークする方法はありますか? あるいは、バケットベースのソリューションよりも古いデータを「期限切れ」にするためのより良い方法を考えられるなら、私はすべて耳にします。

4

1 に答える 1

1

これは少しハックな解決策かもしれませんが、バケット オブジェクトが pickle 化可能である場合 (そして、そうであるように思えます)、それらを pickle 化し、pickle 化されたオブジェクト文字列のバイト長を測定することができます。メモリ内のアンパックされたオブジェクトのサイズと正確に一致しない場合がありますが、オブジェクトが大きくなるにつれて直線的に大きくなり、オブジェクト間の相対的なサイズをかなり正確に把握できるはずです。

非常に大きなオブジェクトをピクルする必要がないように、バケットに追加された各エントリのサイズを、それ自体をピクルし、そのバイト長をバケットの合計バイト長属性に追加することで測定できます。ただし、これを行うと、エントリ自体の独立したサイズには反映されない、エントリとバケットの内部バインディングで使用されるオーバーヘッド メモリが発生することに注意してください。ただし、いくつかのテストを実行してプロファイルを作成できます。これを使用して、実際のサイズを超える新しいエントリごとに %memory オーバーヘッドがどのようになるかを把握します。

于 2013-02-11T03:42:22.407 に答える