web-dev-qa-db-ja.com

なぜ私のpythonプロセスはそれほど多くのメモリを消費するのですか?

pythonを使用して、数百メガバイトにもなることがあるファイルの読み取り、処理、および書き込みを行うプロジェクトに取り組んでいます。特に大きなファイルを処理しようとすると、プログラムが失敗することがあります。ファイル。「メモリエラー」とは書かれていませんが、それが問題だと思います(実際、失敗する理由はまったくありません)。

私は小さなファイルでコードをテストし、「top」を見てメモリ使用量がどのようなものかを確認してきましたが、通常は60%に達します。 topは、合計メモリが4050352kなので、3.8Gbだと言っています。

その間、私はpython自体( 昨日 からの私の質問を参照)内のメモリ使用量を次の少しのコードで追跡しようとしています:

mem = 0
for variable in dir():
    variable_ = vars()[variable]
    try: 
        if str(type(variable_))[7:12] == 'numpy':
            numpy_ = True
        else:
            numpy_ = False
    except:
        numpy_ = False
    if numpy_:
        mem_ = variable_.nbytes
    else:
        mem_ = sys.getsizeof(variable)
    mem += mem_
    print variable+ type: '+str(type(variable_))+' size: '+str(mem_)
print 'Total: '+str(mem)

そのブロックを実行する前に、必要のないすべての変数をNoneに設定し、すべてのファイルと図などを閉じます。そのブロックの後、subprocess.call()を使用して、の次のステージに必要なfortranプログラムを実行します。処理。 fortranプログラムの実行中に上部を見ると、FortranプログラムがCPUの約100%とメモリの約5%を使用しており、pythonがCPUの0%を使用していてメモリの53%。ただし、私の小さなコードスニペットは、pythonのすべての変数を合計すると23Mbになり、約0.5%になるはずだと教えてくれます。

では、何が起こっているのでしょうか。その小さなスニペットがメモリ使用量のスポットを私に与えるとは思わないでしょうが、それは確かに数Mb以内に正確であるはずですか?それとも、トップがメモリが放棄されたことに気付かないが、必要に応じてそれを必要とする他のプログラムで利用できるというだけですか?

要求に応じて、すべてのメモリを消費しているコードの簡略化された部分を次に示します(file_name.cubはISIS3キューブであり、同じマップの5つのレイヤー(バンド)を含むファイルです。最初のレイヤーはスペクトル放射輝度で、次のレイヤーはスペクトル放射輝度です。 4は緯度、経度、その他の詳細と関係があります。これは私が処理しようとしている火星からの画像です。StartByteは、データの開始バイトであるSamplesを示す.cubファイルのasciiヘッダーから以前に読み取った値です。線はマップの寸法であり、ヘッダーからも読み取られます。):

latitude_array = 'cheese'   # It'll make sense in a moment
f_to = open('To_file.dat','w') 

f_rad = open('file_name.cub', 'rb')
f_rad.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_rad.read(StartByte-1))
header = None    
#
f_lat = open('file_name.cub', 'rb')
f_lat.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_lat.read(StartByte-1))
header = None 
pre=struct.unpack('%df' % (Samples*Lines), f_lat.read(Samples*Lines*4))
pre = None
#
f_lon = open('file_name.cub', 'rb')
f_lon.seek(0)
header=struct.unpack('%dc' % (StartByte-1), f_lon.read(StartByte-1))
header = None 
pre=struct.unpack('%df' % (Samples*Lines*2), f_lon.read(Samples*Lines*2*4))
pre = None
# (And something similar for the other two bands)
# So header and pre are just to get to the right part of the file, and are 
# then set to None. I did try using seek(), but it didn't work for some
# reason, and I ended up with this technique.
for line in range(Lines):
    sample_rad = struct.unpack('%df' % (Samples), f_rad.read(Samples*4))
    sample_rad = np.array(sample_rad)
    sample_rad[sample_rad<-3.40282265e+38] = np.nan  
    # And Similar lines for all bands
    # Then some arithmetic operations on some of the arrays
    i = 0
    for value in sample_rad:
        nextline = sample_lat[i]+', '+sample_lon[i]+', '+value # And other stuff
        f_to.write(nextline)
        i += 1
    if radiance_array == 'cheese':  # I'd love to know a better way to do this!
        radiance_array = sample_rad.reshape(len(sample_rad),1)
    else:
        radiance_array = np.append(radiance_array, sample_rad.reshape(len(sample_rad),1), axis=1)
        # And again, similar operations on all arrays. I end up with 5 output arrays
        # with dimensions ~830*4000. For the large files they can reach ~830x20000
f_rad.close()
f_lat.close()
f_to.close()   # etc etc 
sample_lat = None  # etc etc
sample_rad = None  # etc etc

#
plt.figure()
plt.imshow(radiance_array)
# I plot all the arrays, for diagnostic reasons

plt.show()
plt.close()

radiance_array = None  # etc etc
# I set all arrays apart from one (which I need to identify the 
# locations of nan in future) to None

# LOCATION OF MEMORY USAGE MONITOR SNIPPET FROM ABOVE

だから私はいくつかのファイルを開くことについてのコメントに嘘をついた、それは同じファイルの多くのインスタンスです。 Noneに設定されていない1つのアレイのみを続行し、そのサイズは〜830x4000ですが、これはどういうわけか使用可能なメモリの50%を構成します。 gc.collectも試しましたが、変更はありません。そのコード(この問題またはその他に関連する)をどのように改善できるかについてのアドバイスを聞いてとてもうれしく思います。

おそらく私は言及する必要があります:元々私はファイルを完全に開いていました(つまり、上記のように行ごとではありません)、行ごとにそれを行うことはメモリを節約するための最初の試みでした。

12
EddyTheB

変数を区別したからといって、Pythonプロセスが割り当てられたメモリをシステムに戻したという意味ではありません。 Pythonでメモリを明示的に解放するにはどうすればよいですか? を参照してください。 =。

gc.collect()が機能しない場合は、IPCを使用して、子プロセスでのファイルのフォークと読み取り/書き込みを調査してください。これらのプロセスは、終了すると終了し、メモリをシステムに解放します。メインプロセスは、低メモリ使用量で引き続き実行されます。

13
bioneuralnet