13

私は、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 プログラムの実行中に top を見ると、fortran プログラムが CPU の ~100% とメモリの ~5% を使用しており、python は CPU の 0% とメモリの 53% を使用していることがわかります。しかし、私の小さなコード スニペットは、Python のすべての変数の合計が 23Mb しかないことを示しており、これは ~0.5% になるはずです。

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

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

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 も試しましたが、変化はありません。そのコードのいずれかを改善する方法についてアドバイスをいただければ幸いです (この問題に関連するものなど)。

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

4

1 に答える 1

15

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

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

于 2012-08-03T17:39:06.080 に答える