6

私は巨大な辞書グローバル変数を利用するPythonモジュールを持っています。現在、計算コードを一番上のセクションに配置しています。モジュールのインポートまたはリロードに1分以上かかるたびに、これはまったく受け入れられません。次のインポート/リロードで計算する必要がないように、計算結果をどこかに保存するにはどうすればよいですか?cPickleを試しましたが、ファイル(1.3M)から辞書変数をロードするのに計算とほぼ同じ時間がかかります。

私の問題についてもっと情報を与えるために、

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
4

13 に答える 13

17

明確にするために、モジュールの本体のコードは、モジュールがインポートされるたびに実行されるわけではありません。一度だけ実行され、その後、将来のインポートでは、再作成するのではなく、既に作成されたモジュールが検索されます。sys.modules を見て、キャッシュされたモジュールのリストを確認してください。

ただし、問題がプログラムの実行後の最初のインポートにかかる時間である場合は、おそらく python dict 以外の方法を使用する必要があります。おそらく最善の方法は、dbm モジュールの 1 つである sqlite データベースなど、ディスク上のフォームを使用することです。

インターフェイスの変更を最小限に抑えるには、 shelve モジュールが最適なオプションです。これにより、dbm モジュール間に非常に透過的なインターフェイスが配置され、任意の python dict のように動作し、picklable 値を保存できるようになります。次に例を示します。

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

その後、次のプロセスで使用します。検索はディスク上のフォームで要求されたキーに対してのみ実行されるため、大きな遅延は発生しないはずです。したがって、すべてをメモリにロードする必要はありません。

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

これは実際の dict よりも少し遅く、すべてのキーを必要とする処理 (たとえば、印刷しようとする) を行うとロードに時間がかかりますが、問題は解決する可能性があります

于 2008-10-12T20:12:25.073 に答える
4

最初の使用時にグローバル変数を計算します。

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

またはさらに良いことに、特別なデータオブジェクトを介して必要なデータにアクセスします。

class Data:
    GLOBAL_NAME = property(...)

data = Data()

例:

from some_module import data

print(data.GLOBAL_NAME)

Djangoの設定を参照してください。

于 2008-10-12T17:29:46.163 に答える
2

c?Pickleモジュールの代わりにマーシャルモジュールを使用してみることができます。それはもっと速いかもしれません。このモジュールは、Pythonが値をバイナリ形式で保存するために使用します。マーシャルがニーズに合っているかどうかを確認するには、特に次の段落に注意してください。

すべてのPythonオブジェクトタイプがサポートされているわけではありません。一般に、このモジュールで読み書きできるのは、Pythonの特定の呼び出しから値が独立しているオブジェクトのみです。次のタイプがサポートされています:なし、整数、長整数、浮動小数点数、文字列、Unicodeオブジェクト、タプル、リスト、セット、辞書、およびコードオブジェクト。タプル、リスト、および辞書は、長い間のみサポートされることを理解する必要があります。そこに含まれる値自体がサポートされているため。再帰的なリストと辞書は書かないでください(無限ループが発生します)。

念のため、dictをアンマーシャリングする前に、下位互換性の保証がないため、dictをアンマーシャリングするPythonバージョンがマーシャリングを行ったバージョンと同じであることを確認してください。

于 2008-10-12T17:38:20.500 に答える
2

dict リテラルをソースに貼り付けたと思いますが、それで時間がかかりますか? それを回避する方法はわかりませんが、おそらくインポート時にこの辞書をインスタンス化することを避けることができます...実際に初めて使用するときに遅延インスタンス化できます。

于 2008-10-12T16:14:43.523 に答える
2

shelve大きなデータセットでは非常に遅くなります。私はredisをうまく使っており、その周りにFreqDist ラッパーを書きました。非常に高速で、同時にアクセスできます。

于 2009-07-26T15:28:23.640 に答える
2

「棚上げ」ソリューションが遅すぎる、または面倒であることが判明した場合は、他の可能性があります。

于 2008-12-06T01:56:12.390 に答える
1

データ全体をメモリにロードする代わりに、シェルブを使用してデータをディスクに保存できます。したがって、起動時間は非常に高速になりますが、トレードオフとしてアクセス時間は遅くなります。

Shelve は dict の値もピクルしますが、すべてのアイテムの起動時ではなく、各アイテム自体のアクセス時にのみ (アン) ピクルを実行します。

于 2008-10-12T17:04:20.910 に答える
1

私はこれと同じ問題を経験しています...シェルブ、データベースなど...このタイプの問題には遅すぎます。一度ヒットを取得し、それを Redis のようなメモリ内キー/値ストアに挿入する必要があります。メモリ内に存在するだけです (かなりの量のメモリを使い果たす可能性があるため、専用のボックスが必要になる場合があることに注意してください)。リロードする必要はなく、メモリ内でキーを探すだけです。

r = Redis()
r.set(key, word)

word = r.get(key)
于 2010-02-06T16:28:31.870 に答える
1

インポートを高速化するのに役立ついくつかのこと:

  1. Python の実行時に -OO フラグを使用して python を実行してみてください。これにより、モジュールのインポート時間を短縮するいくつかの最適化が行われます。
  2. ディクショナリを、より迅速にロードできる個別のモジュールの小さなディクショナリに分割できなかった理由はありますか?
  3. 最後の手段として、結果が必要になるまでプログラムが遅延しないように、計算を非同期で行うことができます。または、マルチコア アーキテクチャを活用したい場合は、ディクショナリを別のプロセスに配置し、IPC を使用してデータをやり取りすることもできます。

そうは言っても、最初にモジュールをインポートした後、モジュールのインポートに遅延が発生するべきではないことに同意します。その他の一般的な考え方は次のとおりです。

  1. 関数内でモジュールをインポートしていますか? その場合、モジュールが import ステートメントに到達するたびにモジュールがロードされているかどうかを確認する必要があるため、パフォーマンスの問題が発生する可能性があります。
  2. あなたのプログラムはマルチスレッド化されていますか? マルチスレッド アプリでモジュールのインポート時にコードを実行すると、おかしなことやアプリケーションの不安定性が発生することがあります (特に cgitb モジュールの場合)。
  3. これがグローバル変数の場合、グローバル変数のルックアップ時間はローカル変数のルックアップ時間よりも大幅に長くなる可能性があることに注意してください。この場合、同じコンテキストで複数回使用している場合は、ディクショナリをローカル変数にバインドすることで、パフォーマンスを大幅に向上させることができます。

そうは言っても、もう少し文脈がないと具体的なアドバイスをするのは少し難しいです. より具体的には、どこにインポートしていますか? そして、計算は何ですか?

于 2008-10-12T22:31:37.210 に答える
1
  1. 計算集約的な部分を別のモジュールに分割します。その後、少なくともリロード時に、待つ必要はありません。

  2. プロトコル 2 を使用してデータ構造をダンプしてみてください。試行するコマンドは次のようになりますcPickle.dump(FD, protocol=2)。の docstring からcPickle.Pickler:

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    
于 2008-10-14T13:07:08.347 に答える
0

この問題には、別の非常に明白な解決策があります。コードがリロードされても、元のスコープは引き続き使用できます。

したがって...このようなことを行うと、このコードが1回だけ実行されるようになります。

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
于 2009-01-10T18:06:13.157 に答える
0

遅延計算のアイデアを拡張して、dict を、必要に応じて要素を提供 (およびキャッシュ) するクラスに変えてみませんか?

また、サイコを使用して全体的な実行を高速化することもできます...

于 2008-10-12T17:54:36.753 に答える
0

または、値を格納するためにデータベースを使用できますか? データベースへの格納を非常に簡単にする SQLObject をチェックしてください。

于 2008-10-12T18:08:54.853 に答える