11

次のコードによって生成されたデータを処理しようとしています。

for Gnodes in G.nodes()       # Gnodes iterates over 10000 values 
    Gvalue = someoperation(Gnodes)
    for Hnodes in H.nodes()   # Hnodes iterates over 10000 values 
        Hvalue =someoperation(Hnodes)
        score = SomeOperation on (Gvalue,Hvalue)
        dic_score.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])

ディクショナリは大きいため (10000 個のキー X 10000 個のリストにそれぞれ 3 つの要素がある)、メモリに保持するのは困難です。キーと値のペアが生成されるとすぐに(リストの形式で)保存するソリューションを探していました。ZODB を Btree と組み合わせて使用​​することをお勧めします。

これが単純すぎる場合はご容赦ください。私の質問は、transaction.commit()データをコミットするためにいつ呼び出す必要があるかということです。内側のループの最後で呼び出すと、結果のファイルが非常に大きくなります (理由はわかりません)。ここにスニペットがあります:

storage = FileStorage('Data.fs')
db = DB(store)
connection = db.open()
root = connection.root()
btree_container = IOBTree
root[0] = btree_container 
for nodes in G.nodes()
    btree_container[nodes] = PersistentList () ## I was loosing data prior to doing this 

for Gnodes in G.nodes()       # Gnodes iterates over 10000 values 
    Gvalue = someoperation(Gnodes)
    for Hnodes in H.nodes()   # Hnodes iterates over 10000 values 
        Hvalue =someoperation(Hnodes)
        score = SomeOperation on (Gvalue,Hvalue)
        btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
        transaction.commit()

両方のループの外で呼び出すとどうなりますか? 何かのようなもの:

    ......
       ......
          score = SomeOperation on (Gvalue,Hvalue)
          btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
    transaction.commit()

transaction.commit() を呼び出すまで、すべてのデータがメモリに保持されますか? 繰り返しますが、理由はわかりませんが、これによりディスク上のファイル サイズが小さくなります。

メモリに保持されるデータを最小限に抑えたい。ガイダンスをいただければ幸いです。

4

2 に答える 2

30

目標は、メモリの制約内でプロセスを管理できるようにすることです。ZODB をツールとして使用してこれを行うには、ZODB トランザクションがどのように機能し、どのように使用するかを理解する必要があります。

ZODB が大きくなる理由

まず、トランザクション コミットがここで何をするかを理解する必要があります。これは、Data.fs が非常に大きくなる理由も説明します。

ZODB は、変更された永続オブジェクトがディスクに書き込まれるトランザクションごとにデータを書き出します。ここで重要な詳細は、変更された永続オブジェクトです。ZODB は永続オブジェクト単位で動作します。

すべての python 値が永続オブジェクトであるとは限りません。まっすぐな python クラスを定義すると、永続化されず、int や list などの組み込みの python 型も永続化されません。一方、継承元として定義したクラスpersistence.Persistent 永続オブジェクトです。コードで使用するクラスBTreesと同様に、一連のクラスはから継承しますPeristentListPersistent

これで、トランザクションのコミット時に、変更されたすべての永続オブジェクトがそのトランザクションの一部としてディスクに書き込まれます。したがって、PersistentList追加されたオブジェクトはすべてディスクに完全に書き込まれます。BTreesこれをもう少し効率的に処理します。それらは、それ自体が永続的なバケットを保存し、実際に保存されたオブジェクトを保持します。したがって、作成するいくつかの新しいノードごとに、BTree 構造全体ではなく、バケットがトランザクションに書き込まれます。ツリーに保持されているアイテム自体が永続オブジェクトであるため、それらへの参照のみがバケット レコードに格納されることに注意してください。

現在、ZODB はトランザクション データをData.fsファイルに追加して書き込みますが、古いデータは自動的に削除されません。ストアから特定のオブジェクトの最新バージョンを見つけることで、データベースの現在の状態を構築できます。これが、あなたが非常に成長している理由であり、トランザクションがコミットされるData.fsにつれて、ますます大きなPersistentListインスタンスの新しいバージョンを書き出しています。

古いデータを削除することをパッキングVACUUMと呼びます。これはPostgreSQL やその他のリレーショナル データベースのコマンドに似ています。単に変数の.pack()メソッドを呼び出してすべての古いリビジョンdbを削除するか、そのメソッドのおよびパラメータを使用して、保持する履歴の量に制限を設定します。最初はタイムスタンプ (エポックからの秒数) であり、その前にパックできます。現在の時刻から保持する過去の日数、または指定されている場合。古いトランザクションの部分的なリストが削除されるため、パッキングによってデータ ファイルが大幅に削減されます。パッキングはコストのかかる操作であるため、データセットのサイズによっては時間がかかる場合があることに注意してください。tdaystime.time()dayst

トランザクションを使用してメモリを管理する

永続性を使用してメモリの制約を回避し、トランザクションを使用して物事をディスクにフラッシュしようとすることで、非常に大きなデータセットを構築しようとしています。ただし、通常、トランザクション コミットを使用すると、データセットの構築が完了したことが通知されます。これは、1 つのアトミックな全体として使用できるものです。

ここで使用する必要があるのはsavepointです。セーブポイントは基本的にサブトランザクションであり、トランザクション全体の中でデータを一時的にディスクに保存するよう要求できるポイントです。トランザクションをコミットすると、永続化されます。セーブポイントを作成するには、トランザクションで.savepointメソッドを呼び出します。

for Gnodes in G.nodes():      # Gnodes iterates over 10000 values 
    Gvalue = someoperation(Gnodes)
    for Hnodes in H.nodes():  # Hnodes iterates over 10000 values 
        Hvalue =someoperation(Hnodes)
        score = SomeOperation on (Gvalue,Hvalue)
        btree_container.setdefault(Gnodes, PersistentList()).append(
            [Hnodes, score, -1 ])
    transaction.savepoint(True)
transaction.commit()

上記の例では、optimisticフラグを True に設定しました。つまり、このセーブポイントにロールバックするつもりはありません。一部のストレージはロールバックをサポートしていないため、これが不要であることを通知することで、そのような状況でコードを機能させることができます。

transaction.commit()また、データセット全体が処理されたときに発生することにも注意してください。これは、コミットが達成するはずのものです。

セーブポイントが行うことの 1 つは、ZODB キャッシュのガベージ コレクションを呼び出すことです。これは、現在使用されていないデータがメモリから削除されることを意味します。

そこの「現在使用されていない」部分に注意してください。コードのいずれかが変数に大きな値を保持している場合、データをメモリからクリアできません。あなたが私たちに示したコードから判断できる限り、これは問題ないようです。しかし、あなたの操作がどのように機能するか、またはノードをどのように生成するかはわかりません。たとえば、イテレータが行う場合に完全なリストをメモリ内に構築しないように注意してください。または、たとえば、リストのすべてのリストが参照される大きな辞書を構築します。

セーブポイントを作成する場所について少し実験できます。を処理するたびに作成することも、上記で行ったようにループを処理HNodesしたときにのみ作成することもできます。GNodesごとにリストを構築しているGNodesため、とにかくすべてをループしている間はメモリに保持され、H.nodes()ディスクへのフラッシュはおそらく、完全に構築を完了して初めて意味があります。

ただし、より頻繁にメモリをクリアする必要がある場合は、の代わりにBTrees.OOBTree.TreeSetクラスまたはクラスを使用して、データをより永続的なオブジェクトに分割することを検討する必要があります。Aは順序付けされていますが (簡単に) インデックス可能ではありませんが、 aは単純なインクリメント インデックス キーを使用してリストとして使用できます。BTrees.IOBTree.BTreePersistentListTreeSetBTree

for i, Hnodes in enumerate(H.nodes()):
    ...
    btree_container.setdefault(Gnodes, IOBTree())[i] = [Hnodes, score, -1]
    if i % 100 == 0:
        transaction.savepoint(True)

上記のコードは、PersistentList の代わりに BTree を使用し、100HNodes処理ごとにセーブポイントを作成します。BTree は、それ自体が永続的なオブジェクトであるバケットを使用するため、構造全体をより簡単にセーブポイントにフラッシュできます。すべてH.nodes()を処理するためにメモリにとどまる必要はありません。

于 2012-06-29T11:00:30.700 に答える
1

トランザクションを構成するものは、アプリケーションで何が「アトミック」である必要があるかによって異なります。トランザクションが失敗すると、以前の状態 (最後のコミットの直後) にロールバックされます。アプリケーション コードから、各 Gnode の値を計算したいことがわかります。したがって、コミットは次のように Gnodes ループの最後に入ることができます。

for Gnodes in G.nodes():       # Gnodes iterates over 10000 values 
    Gvalue = someoperation(Gnodes)
    for Hnodes in H.nodes():   # Hnodes iterates over 10000 values 
        Hvalue =someoperation(Hnodes)
        score = SomeOperation on (Gvalue,Hvalue)
        btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
    # once we calculate the value for a Gnodes, commit
    transaction.commit()

コードから、「Hvalue」の組み合わせが Gvalue または Gnodes に依存していないように見えます。コストのかかる操作の場合、計算に影響を与えなくても、Gnode ごとに 1000 回計算することになります。だから、私はそれをループの外に移動します。

# Hnodes iterates over 10000 values
hvals = dict((Hnodes, someoperation(Hnodes)) for Hnodes in H.nodes())
# now you have mapping of Hnodes and Hvalues

for Gnodes in G.nodes():       # Gnodes iterates over 10000 values 
    Gvalue = someoperation(Gnodes)
    for Hnodes, Hvalue in hvals.iteritems(): 
        score = SomeOperation on (Gvalue,Hvalue)
        btree_container.setdefault(Gnodes,[]).append([Hnodes, score, -1 ])
    # once we calculate the value for a given Gnodes, commit
    transaction.commit()
于 2012-06-29T06:53:43.500 に答える