質問
巨大な辞書グローバル変数を使用するpythonモジュールがあります。現在、モジュールのインポートまたはリロードに1分以上かかるたびに計算コードを最上部に配置しますが、これはまったく受け入れられません。次のインポート/リロードで計算する必要がないように、計算結果をどこかに保存するにはどうすればよいですか? cPickleを試しましたが、ファイル(1.3M)から辞書変数をロードするには、計算とほぼ同じ時間がかかります。
問題に関する詳細情報を提供するには、
FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
解決
明確にするために:モジュールの本文のコードは、モジュールがインポートされるたびに実行されることはありません -それは一度だけ実行され、その後、インポートは既に作成されたモジュールを見つけるのではなく、再作成します。キャッシュされたモジュールのリストを見るには、sys.modulesをご覧ください。
ただし、プログラムの実行後の最初のインポートにかかる時間が問題である場合は、おそらくPython dict以外の方法を使用する必要があります。おそらく最良の方法は、dbmモジュールの1つであるsqliteデータベースなどのディスク上のフォームを使用することです。
インターフェースの最小限の変更については、シェルブモジュールが最適なオプションである可能性があります-これにより、dbmモジュール間に任意のpython dictのように動作するかなり透過的なインターフェースが配置され、ピクル可能な値を保存できます。次に例を示します。
# 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
実際の辞書よりも少し遅く、すべてのキーを必要とするもの(たとえば、印刷しようとする)を行うと の読み込みに時間がかかりますが、解決する可能性がありますあなたの問題。
他のヒント
最初の使用時にグローバル変数を計算します。
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設定を参照してください。
ソースに辞書リテラルを貼り付けたと思いますが、それは少し時間がかかっていますか?私はそれを回避する方法がわかりませんが、おそらく import でこの辞書をインスタンス化することを避けることができます...実際に使用されたときに、それを遅延インスタンス化できます。
cの代わりに marshal モジュールを使用してみてください。ピクルス;より速くなる可能性があります。このモジュールは、値をバイナリ形式で保存するためにpythonによって使用されます。特に次の段落に注意して、マーシャルがニーズに合うかどうかを確認してください。
すべてのPythonオブジェクトタイプがサポートされているわけではありません。一般に、Pythonの特定の呼び出しから独立した値を持つオブジェクトのみが、このモジュールで読み書きできます。次のタイプがサポートされています:なし、整数、長整数、浮動小数点数、文字列、Unicodeオブジェクト、タプル、リスト、セット、ディクショナリ、およびコードオブジェクト。タプル、リストおよびディクショナリはlongとしてのみサポートされることを理解してくださいその中に含まれる値自体がサポートされているため。再帰的なリストと辞書を書くべきではありません(無限ループを引き起こします)。
安全のために、辞書を非整列化する前に、辞書を非整列化するPythonバージョンが、マーシャリングを行ったPythonバージョンと同じであることを確認してください。下位互換性の保証はありません。
shelve
は、大きなデータセットでは非常に遅くなります。 redis をかなり使用しています成功し、その周りに FreqDistラッパーを作成しました。非常に高速で、同時にアクセスできます。
棚を使用して保存できますデータ全体をメモリにロードする代わりに、ディスク上のデータ。そのため、起動時間は非常に高速になりますが、トレードオフはアクセス時間が遅くなります。
Shelveはdict値もピクルしますが、すべてのアイテムの起動時ではなく、各アイテム自体のアクセス時にのみ(un)pickleを実行します。
インポートの高速化に役立ついくつかのこと:
- Pythonの実行中に-OOフラグを使用してPythonを実行してみてください。これにより、モジュールのインポート時間を短縮する最適化が行われます。
- 辞書をより速くロードできる個別のモジュールの小さな辞書に分割できなかった理由はありますか?
- 最後の手段として、結果が必要になるまでプログラムを遅延させないように、非同期で計算を行うことができます。または、マルチコアアーキテクチャを活用したい場合は、辞書を別のプロセスに配置し、IPCを使用してデータをやり取りすることもできます。
そうは言っても、最初にモジュールをインポートした後、モジュールのインポートで遅延が発生することはないはずです。その他の一般的な考え方は次のとおりです。
- モジュールを関数内にインポートしていますか?そうである場合、モジュールがimportステートメントにヒットするたびにロードされるかどうかを確認する必要があるため、この はパフォーマンスの問題につながる可能性があります。
- プログラムはマルチスレッドですか?マルチスレッドアプリでモジュールをインポートする際にコードを実行すると、なんとなく不安定になり、アプリケーションが不安定になる場合があります(特にcgitbモジュールの場合)。
- これがグローバル変数である場合、グローバル変数のルックアップ時間はローカル変数のルックアップ時間よりも大幅に長くなる可能性があることに注意してください。この場合、同じコンテキストで辞書を複数回使用している場合、辞書をローカル変数にバインドすることにより、パフォーマンスを大幅に改善できます。
そうは言っても、もう少しコンテキストなしに具体的なアドバイスをするのは少し難しいです。具体的には、どこにインポートしていますか?そして、計算は何ですか?
-
計算量の多い部分を別のモジュールにまとめます。その後、少なくともリロード時には、待つ必要はありません。
-
プロトコル2を使用してデータ構造のダンプを試行します。試行するコマンドは
cPickle.dump(FD、protocol = 2)
です。cPickle.Pickler
のdocstringから: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.
この同じ問題を経験しています... シェルフ、データベースなどはすべて、このタイプの問題には遅すぎます。ヒットを1回取得し、Redisなどのメモリ内のキー/ valストアに挿入する必要があります。メモリ内にそのまま存在します(十分な量のメモリが消費される可能性があるため、専用のボックスが必要な場合があります)。再ロードする必要はありません。メモリ内でキーを探すだけです
r = Redis()
r.set(key, word)
word = r.get(key)
遅延計算のアイデアを拡張して、必要に応じて要素を提供(およびキャッシュ)するクラスに辞書を変えてみませんか?
また、psycoを使用して全体の実行を高速化することもできます...
または値を保存するためにデータベースを使用できますか? SQLObjectを確認してください。SQLObjectを使用すると、データベースにデータを非常に簡単に保存できます。
この問題には別の非常に明白な解決策があります。コードがリロードされても、元のスコープは引き続き使用可能です。
したがって...このようなことを行うと、このコードが確実に1回だけ実行されます。
try:
FD
except NameError:
FD = FreqDist(word for word in brown.words())