7

メモリに保持されている大きなリストからデータにアクセスするプロジェクトに取り組んでいます。リストは非常に大量 (数百万行) であるため、メモリの使用量を常に監視しています。私は OS X を使用しているので、これらのリストを作成するときはアクティビティ モニターを開いたままにしています。

リストが使用するメモリの量は、リストの構成方法によって大きく異なることに気付きましたが、その理由がわかりません。

次に、いくつかのコード例を示します。

(OSX 10.8.3 で Python 2.7.4 を使用しています)

以下の最初の関数は、リストを作成し、同じ 3 つの乱数すべてで埋めます。

以下の 2 番目の関数は、リストを作成し、すべての異なる乱数で埋めます。

import random
import sys


def make_table1(size):
    list1 = size *[(float(),float(),float())] # initialize the list
    line = (random.random(), 
            random.random(), 
            random.random())
    for count in xrange(0, size): # Now fill it
        list1[count] = line
    return list1

def make_table2(size):
    list1 = size *[(float(),float(),float())] # initialize the list
    for count in xrange(0, size): # Now fill it
        list1[count] = (random.random(), 
                        random.random(), 
                        random.random())
    return list1

(最初に、上記のコードをもっと効率的に記述できたことを認識しています。2 つの例をできるだけ似たものにするために、このように記述しています。)

次に、これらの関数を使用していくつかのリストを作成します。

In [2]: thing1 = make_table1(6000000)

In [3]: sys.getsizeof(thing1)
Out[3]: 48000072

この時点で、私のメモリ使用量は約 46 MB ジャンプしました。これは、上記の情報から予想されることです。

次の関数に進みます。

In [4]: thing2 = make_table2(6000000)

In [5]: sys.getsizeof(thing2)
Out[5]: 48000072

ご覧のとおり、2 つのリストが使用するメモリは同じです。それらはまったく同じ長さなので、それは予想されることです. 私が予期していなかったのは、Activity Monitor によって与えられたメモリ使用量が 1 GB を超えたことです!

多少のオーバーヘッドがあることは理解していますが、20倍ですか?46MBのリストで1GB?

真剣に?

さて、診断に進みます...

私が最初に試みたのは、ガベージを収集することです。

In [5]: import gc

In [6]: gc.collect()
Out[6]: 0

使用されるメモリ量に違いはありませんでした。

次に、guppy を使用してメモリの移動先を確認しました。

In [7]: from guppy import hpy

In [8]: hpy().heap()

Out[8]: 
Partition of a set of 24217689 objects. Total size = 1039012560 bytes.
 Index  Count   %     Size   % Cumulative  % Kind (class / dict of class)
     0 6054789  25 484821768  47 484821768  47 tuple
     1 18008261  74 432198264  42 917020032  88 float
     2   2267   0 96847576   9 1013867608  98 list
     3  99032   0 11392880   1 1025260488  99 str
     4    585   0  1963224   0 1027223712  99 dict of module
     5   1712   0  1799552   0 1029023264  99 dict (no owner)
     6  13606   0  1741568   0 1030764832  99 types.CodeType
     7  13355   0  1602600   0 1032367432  99 function
     8   1494   0  1348088   0 1033715520  99 type
     9   1494   0  1300752   0 1035016272 100 dict of type
<691 more rows. Type e.g. '_.more' to view.>

さて、私の記憶は次のものに奪われています:

タプルの 462 MB (え?)

412 MB のフロート (何?)

92 MB のリスト (わかりました、これは理にかなっています。2*46MB = 92)

私のリストは事前に割り当てられているので、過剰な割り当てが行われているとは思いません。

質問:

これら 2 つの非常によく似たリストで使用されるメモリの量がなぜそれほど異なるのでしょうか?

オーバーヘッドがあまりないリストを作成する別の方法はありますか?

そのメモリをすべて解放する方法はありますか?

注: ディスクに保存したり、array.array、numpy、pandas データ構造を使用したりすることはお勧めしません。これらはすべて素晴らしいオプションですが、この質問はそれらに関するものではありません。この質問は、単純な古いリストに関するものです。

Python 3.3 で同様のコードを試しましたが、結果は同じです。

同様の問題を抱えている人がいます。いくつかのヒントが含まれていますが、同じ質問ではありません。

皆さん、ありがとうございました!

4

1 に答える 1

9

どちらの関数も、6000000 件の参照のリストを作成します。

sizeof(thelist) ≅ sizeof(reference_to_a_python_object) * 6000000

最初のリストには、3 つの float の同じ 1 つのタプルへの 6000000 の参照が含まれています。

2 番目のリストには、18000000 の異なる float を含む 6000000 の異なるタプルへの参照が含まれています。

ここに画像の説明を入力

ご覧のとおり、float には 24 バイト、triple には 80 バイトが必要です (Python のビルドを使用)。いいえ、numpy 以外にそれを回避する方法はありません。

リストを収集可能なガベージにするには、リストへの参照をすべて取り除く必要があります。

del thing1 
del thing2
于 2013-05-11T01:25:21.873 に答える