2012-03-03 14 views
2

私は、各インスタンスにデータを格納するPython GAEアプリケーションを使用しています。実例として、私は私のアプリに追加した。このテストコードを考えてみます。クエリ変数nの値でこのハンドラへの呼び出しは、それぞれ10のリストにn個の文字列を追加するには、インスタンスが発生します Google App Engineで予期せずメモリ使用量が高い

from google.appengine.ext import webapp 

bucket = [] 

class Memory(webapp.RequestHandler): 
    def get(self): 
     global bucket 
     n = int(self.request.get('n')) 
     size = 0 
     for i in range(n): 
      text = '%10d' % i 
      bucket.append(text) 
      size += len(text) 
     self.response.out.write('Total number of characters = %d' % size) 

文字は長い。

これをn = 1(ロードするすべてを取得する)と呼び、プロダクションサーバーでインスタンスメモリの使用状況を確認すると、29.4MBという数字が表示されます。 n = 100000で呼び出してもう一度チェックすると、メモリ使用量は38.9MBになりました。つまり、私のメモリ占有量は9.5MB増えて100万文字しか保存できませんでした。これは私が期待していたものの10倍近くです。文字はそれぞれ1バイトしか消費しないと信じていますが、間違っていてもまだまだ道のりはまだまだです。リスト構造のオーバーヘッドは確かに説明できません。明示的なガベージコレクションコールを追加しようとしましたが、数値は変更されませんでした。何が欠けていますか?フットプリントを減らす方法はありますか?

(ちなみに、私はリストの代わりにセットを使ってみましたが、n = 100000で呼び出すとメモリ使用量が13MB増加しました。これは、100000文字列の設定オーバーヘッドがリストより3.5MB多く、また、予想よりもはるかに大きい)。

答えて

1

は、長さ1の文字列を考えてみましょう...私は遅く、ここでパーティに本当にだということを知っているが、これは全く驚くべきことではない:

s = '1' 

きれいです小さく、右?たぶんどこか1バイトのオーダーですか?いいえ。

>>> import sys 
>>> sys.getsizeof('1') 
38 

だから、あなたが作成した各文字列(これらの文字列のすべてのメソッドは、どこかを保存する必要があります)に伴うオーバーヘッドの約37バイトです。

さらに、CPUがバイトサイズではなく「ワードサイズ」に基づいてアイテムを格納するのが最も効率的です。たくさんのシステムでは、 "単語"は4バイトです...)。私は確かに分かりませんが、Pythonのメモリアロケータがあまりにも速く動作するようにトリックを演奏するなら、私は驚くことはありません。

また、リストは過剰割り当て配列として表されることを忘れないでください(.appendの度に大きなパフォーマンスの問題を防ぐため)。あなたが100k要素のリストを作る時、pythonは実際に110k以上のポインタを割り当ててしまう可能性があります。 setに関する最後

、 - それはおそらくかなり容易setは(彼らはすべての後にすべてのそれらのハッシュの衝突を回避する必要があります)listよりも過剰に割り当てられ、さらにあるという事実によって説明されます。

>>> sys.getsizeof(set([1])) 
232 
>>> sys.getsizeof(set([1, 2])) 
232 
>>> sys.getsizeof(set([1, 2, 3])) 
232 
>>> sys.getsizeof(set([1, 2, 3, 4])) 
232 
>>> sys.getsizeof(set([1, 2, 3, 4, 5])) 
232 
>>> sys.getsizeof(set([1, 2, 3, 4, 5, 6])) # resize! 
744 
0

リスト構造のオーバーヘッドは、あなたが何を直接見ているのかを説明していませんが、memory fragmentationはありません。また、文字列の長さを数えることはかなり少なくなります。

+0

私は文字カウントが「大幅に」過少にメモリ使用量かもしれないことに同意します:彼らは、設定されたサイズは、ハッシュ衝突を回避するために、アレイ内の十分な空きスロットを持つために成長するように、メモリの使用量に大きなジャンプを持つ終わります、しかし10のファクターによって?あなたのインスタンスのメモリは、断片化のために実際のデータが10%、ゴミが90%だと言っていますか? trueの場合、問題は、Google App Engine内の非効率的なメモリ割り当てアルゴリズムほど断片化されていない可能性があります。malloc()は決して悪くありませんでした。 – Dragonfly

+0

「malloc」に関する私の経験は異なります。そのサンプルコードが何をしているか再度見てください。これは、リストをバックアップする基礎となるメモリ領域の定期的な成長を引き起こし、データをコピーしている間に新旧のコピーを少なくとも生きているように保ちます。間に新しい文字列を作成しています。それはヒープを分割することになります。 –

0

私は専門家ではありませんが、これは興味深い質問です。それは、GAEの問題よりも、Pythonのメモリ管理の問題のようです。あなたはそれをローカルで実行し、GAEにデプロイされたローカルdev_appserverのメモリ使用量を比較しようとしましたか?それはそれがGAEプラットフォームか、単にPythonかを示すはずです。

第2に、使用したPythonコードはシンプルですが、効率的ではありません。forループの代わりにリストの理解がより効率的でなければなりません。これにより、メモリの使用量が少し減るはずです。

''.join([`%10d` % i for i in range(n)]) 

拡大している文字列を常に再割り当てする必要があります。 forループのたびに、破棄された文字列が周りに残っています。私はあなたのforループの後に余分な文字列をきれいにする必要があった後にガベージコレクタをトリガすることを期待していたでしょう。

メモリ使用量を確認する前に、ガベージコレクタをトリガしてみてください。

import gc 
gc.collect() 
return len(gc.get_objects()) 

ガベージコレクタが余分な文字列の一部を削除していないかどうかを知ることができます。

0

これは主にドラゴンズの反応です。

サンプルコードは問題を説明するためだけに存在するので、私は効率が低いとは心配していませんでした。私は代わりに、アプリケーションが実際のデータの約10倍のメモリを消費する理由について懸念しています。私はいくつかのメモリオーバーヘッドがあることを理解することができますが、これはずっとですか?

しかし、私はリスト内包(結合なしで私のオリジナルにマッチする)を使用しようとしましたが、メモリー使用量は9.5MBから9.6MBにわずかに増加しました。多分それは誤差の範囲内にあるでしょう。あるいは、大規模な()の式はそれを吸います。それは、間違いなく、リリースされましたが、xrange()を使用する方が良いと思います。結合によって、インスタンス変数は1つの非常に長い文字列に設定され、メモリのフットプリントは意外にも実用的な1.1MBまで低下しますが、これは全く同じではありません。リストの理解を使わずに、インスタンス変数を100万文字に設定するだけで、同じ1.1MBが得られます。

私のループには「捨てられた文字列が残っている」ということに私は同意できません。私は、文字列がリストに追加されたと考えています(言い換えれば、参考にしてください)、文字列は破棄されません。

私は、元の質問にあるように、私はすでに明示的なガベージコレクションを試みていました。そこに助けはありません。

ここに結果があります。文字列の長さを10から他の数に変更すると、メモリ使用量に比例した変更が行われますが、そこにも定数があります。私の実験では、リストに追加されたすべての文字列には、文字列の長さにかかわらず、85バイトのオーバーヘッドがあることが示されています。これは文字列や文字列をリストに入れるコストですか?私は後者に向かって傾いている。 100,000 Noneのリストを作成すると、4.5MB、またはNoneあたり約45バイトが消費されます。これは文字列の場合ほど悪くはありませんが、それでもかなり悪いです。前にも述べたように、リストよりもセットが悪いです。

なぜ私はオーバーヘッド(またはフラグメンテーション)がこの悪いのか理解したいと思いますが、避けられない結論は、小さなオブジェクトの大きなコレクションが非常に高価であるように思われます。おそらくこれはGAEの問題よりもPythonの問題の方がはるかに正しいでしょう。

+0

こんにちは、私はあなたのコード例を完全に間違って読んでいますが、バケツがリストであったという事実に注意を払っておらず、基盤を離れていました。 – dragonx

+1

2.7ランタイムを使用している場合は、sys.getsizeof()を使用してオブジェクトのサイズを調べることができます。 予想以上に高いメモリ使用量はPythonのためであり、GAEに固有のものではありません。 Python文字列は単に文字列ではなく文字列オブジェクトです。 Pythonシェルでdir( "a")を実行して、文字列オブジェクトに何が付いているかを理解してから、サイズが一列になっていることを確認します。 – dragonx

関連する問題