Main Content

メモリ不足エラーの解決

問題

コードで大量のデータが処理される場合やメモリが効率的に使用されていない場合、MATLAB® は、無理な配列サイズに応答してエラーを生成するか、メモリ不足になる可能性があります。MATLAB には、大きすぎる配列が作成されるのを防ぐ、組み込みの保護機能があります。たとえば、次のコードはエラーになります。要求された数の要素をもつ配列を MATLAB で作成できないためです。

A = rand(1e9);
Requested array exceeds the maximum possible variable size.

既定では、MATLAB はコンピューターの RAM (バーチャル メモリを除く) を最大 100% まで使用して配列にメモリを割り当てることができ、配列サイズがそのしきい値を超えると、MATLAB ではエラーが発生します。たとえば、次のコードは、サイズが配列サイズの上限を超える配列の作成を試みます。

B = rand(1e6);
Requested 1000000x1000000 (7450.6GB) array exceeds maximum array size preference (63.7GB). This might cause MATLAB to become
unresponsive.

[MATLAB ワークスペース基本設定] で配列サイズの制限をオフにすると、大きすぎる配列の作成の試行によって MATLAB がメモリ不足になるか、過度のメモリ ページング (つまり、RAM とディスク間のメモリ ページの移動) が原因で MATLAB またはコンピューターが応答しなくなる可能性があります。

B = rand(1e6);
Out of memory.

考えられる解決策

メモリ制限の問題がいかに生じたかに関わりなく、MATLAB では状況と目的に応じていくつかの解決策を利用できます。たとえば、コードによるメモリの使用方法の向上、データストアや tall 配列などの特殊なデータ構造体の活用、計算用クラスター内のプールされたリソースの利用、設定と基本設定の調整が可能です。

メモ

ここで示す解決策は MATLAB に固有のものです。システム全体のメモリ パフォーマンスを最適化するには、コンピューターに物理メモリ (RAM) を追加するか、オペレーティング システム レベルで調整することを検討してください。

不要な変数のクリア

変数が不要になった際にはクリアするようにします。メモリから項目をクリアするには、関数 clear を使用します。

更新前更新後
A = rand(1e4);
disp(max(A,[],"all"))
B = rand(1e4);
A = rand(1e4);
disp(max(A,[],"all"))
clear A
B = rand(1e4);

詳細については、メモリを効率的に使用するための対策を参照してください。

適切なデータ ストレージの使用

メモリ要件は MATLAB データ型によって異なります。適切なデータ型とストレージを使用することで、コードによって使用されるメモリ量を減らせる場合があります。この節の解決策の詳細については、メモリを効率的に使用するための対策を参照してください。

適切な数値クラスの使用.  操作の内容の応じて異なる数値クラスを使用する必要があります。MATLAB では、double は既定の数値データ型であり、ほとんどの計算タスクで十分な精度を提供します。

  • 線形代数のように複雑な計算を行う場合は、倍精度 (double) または単精度 (single) のいずれかの形式の浮動小数点数を使用します。single 型の数値では、double 型の数値より必要メモリが少なくなりますが、精度が低くなります。

  • 簡単な演算のみを実行し、元のデータを整数として表す場合は、MATLAB の整数クラスを使用します。

クラス (データ型)バイトサポートされている演算
single4ほとんどの数学演算
double8すべての数学演算
logical1論理演算と条件付き演算
int8, uint81算術関数と一部の単純な関数
int16, uint162算術関数と一部の単純な関数
int32, uint324算術関数と一部の単純な関数
int64, uint648算術関数と一部の単純な関数

データを格納する際のオーバーヘッドの削減.  数値配列や文字配列を作成する場合、MATLAB では、配列データを保存するためのメモリのブロックが割り当てられます。また、MATLAB では、クラスや次元などの配列データに関する情報も "ヘッダー" と呼ばれる別の小さなメモリ ブロックに保存されます。簡単な数値配列や文字配列ではオーバーヘッドが最小になるので、できるだけこの配列を使用します。他のデータ構造体は、複雑すぎて単純な配列に保存できないデータの場合にのみ使用します。

構造体と cell 配列について、MATLAB では配列に対してのみでなく、構造体の各フィールド、cell 配列の各 cell に対してもヘッダーが作成されます。このため、構造体または cell 配列を格納するために必要なメモリ量は、保存するデータ量だけでなく作成方法にも依存します。その結果、多くの小さな要素をもつ cell 配列、またはコンテンツがほとんど含まれない多くのフィールドをもつ構造体は、オーバーヘッドが大きくなるので避けるようにします。

更新前更新後
% S has 15,000 fields (3 fields per array element)
for i = 1:100
    for j = 1:50
        S(i,j).R = 0;  % Each field contains a numeric scalar
        S(i,j).G = 0;
        S(i,j).B = 0;
    end
end
% S has 3 fields 
S.R = zeros(100,50);  % Each field contains a numeric array
S.G = zeros(100,50);
S.B = zeros(100,50);

可能であれば配列をスパースにする.  非ゼロ要素が少ない行列はスパース ストレージを使用して保存することをお勧めします。非スパース行列がもつ非ゼロ要素の数が少ない場合、行列をスパース ストレージに変換することで、通常はメモリの使用量とコードの実行時間が改善されます。MATLAB には、スパース ストレージをサポートする関数がいくつかあります。たとえば、関数 speye を使用してスパース単位行列を作成できます。

更新前更新後
I = eye(1000);
I = speye(1000);

適切な MATLAB クラスを使用したデータのインポート.  fread を使用してバイナリ ファイルからデータを読み込む場合、ファイル内のデータのクラスのみを指定し、データがワークスペースに読み込まれた後に MATLAB で使用されるデータ クラスを忘れてしまうというミスを犯しがちです。メモリ内のデータのクラスを指定しない場合、8 ビットの値を読み取るとしても、MATLAB では double が使用されます。

更新前更新後
fileID = fopen("large_file_of_uint8s.bin","r"); 
A = fread(fileID,1e3,"uint8"); 
fileID = fopen("large_file_of_uint8s.bin","r"); 
A = fread(fileID,1e3,"uint8=>uint8"); 

データの不要なコピーの回避

メモリの使用量と実行速度を改善するには、コードでデータの不要なコピーが行われないようにします。この節の解決策の詳細については、データの不要なコピーの回避およびメモリを効率的に使用するための対策を参照してください。

一時的な配列の作成の回避.  一時的な配列が不要な場合は、作成しないようにします。たとえば、0 で構成された配列を一時変数として作成し、その変数を関数に渡す代わりに、1 つのコマンドを使用して両方の演算を実行します。

更新前更新後
A = zeros(1e6,1);
As = single(A);
As = zeros(1e6,1,"single");

メモリの事前割り当て.  大規模なデータ セットを扱う場合、配列のサイズを繰り返し変更することでプログラムがメモリ不足になる可能性があります。元の位置の使用可能な連続メモリを超えて配列を拡張する場合、MATLAB では、配列のコピーを作成し、そのコピーを十分なスペースのあるメモリ ブロックに移動させなければなりません。このプロセスの間は、メモリに元の配列のコピーが 2 つ存在します。配列に必要なスペースの最大容量を事前に割り当てることで、メモリの使用量とコードの実行時間を改善できます。詳細については、事前割り当てを参照してください。

更新前更新後
x = 0;
for k = 2:1000000
   x(k) = x(k-1) + 5;
end
x = zeros(1,1000000);
for k = 2:1000000
   x(k) = x(k-1) + 5;
end

入力引数を減らすための入れ子関数の使用.  関数を呼び出すと、その関数で値を変更する際に、通常、MATLAB によって変数の一時的なコピーが呼び出し元のワークスペースに作成されます。MATLAB では、さまざまな手法によって不要なコピーの作成が回避されますが、入力変数の一時的なコピーは常に回避できるとは限りません。

関数呼び出しで一時的なコピーを回避する方法の 1 つとして、入れ子関数を使用することが考えられます。入れ子関数は外側の関数すべてとワークスペースを共有するため、関数呼び出しにおいて変数のコピーを渡す必要はありません。詳細については、入れ子関数を参照してください。

必要なデータのみの読み込み

メモリの問題を解決する方法の 1 つとして、問題を解くために必要な部分のみを大規模なデータ セットから MATLAB にインポートすることが考えられます。クエリに一致する要素を明示的に検索できるデータベースなどのソースからインポートする場合、通常、データ セットのサイズは問題にはなりません。ただし、大規模なテキスト ファイルやバイナリ ファイルを読み込む場合には共通する問題となります。

関数 datastore を使うと、大規模なデータ セットを段階的に操作できるようになります。データストアは、データ セットの一部のみを一度にメモリに読み込む場合に常に有用です。

データストアを作成するには、ファイルの名前、または類似形式のファイルのコレクションを含むディレクトリの名前を指定します。たとえば、単一のファイルでは以下を使用します。

ds = datastore("path/to/file.csv");
あるいは、フォルダー内のファイルのコレクションでは以下を使用します。
ds = datastore("path/to/folder/");
また、ワイルドカード文字 * を使用して特定タイプのファイルをすべて選択することもできます。
ds = datastore("path/to/*.csv");
データストアでは、さまざまなファイル形式 (表形式データ、イメージ、スプレッドシートなど) がサポートされています。詳細については、ファイル形式またはアプリケーション用のデータ ストアの選択を参照してください。

データストア以外にも、MATLAB には、ファイルの一部を読み込む他の関数がいくつかあります。次の表は、これらの関数を操作対象のファイルの種類別にまとめています。

ファイルの種類部分読み込み
MAT ファイル

関数 matfile で作成するオブジェクト内にインデックスを付けることにより、変数の部分を読み込みます。詳細については、MAT ファイルへの変数の一部の読み込みと保存を参照してください。

テキスト

選択された列と行のみを読み込むことによって、大規模なテキスト ファイルの一部にアクセスするには、関数 textscan を使用します。textscan で、行数、または繰り返される形式番号を指定すると、MATLAB で必要な正確なメモリ量があらかじめ計算されます。

バイナリ

既知の形式をもつファイルの一部にアクセスするには、fread などの低レベル バイナリ ファイル I/O 関数を使用できます。形式がわからないバイナリ ファイルの場合は、関数 memmapfile を使ってメモリ マッピングを試してみます。

イメージ、オーディオ、ビデオ、HDF

MATLAB 関数の多くはこのようなタイプのファイルからの読み込みをサポートし、それらを使用して、読む取るデータの一部を選択できます。詳細は、インポートとエクスポートでサポートされるファイル形式にリストされている関数のリファレンス ページを参照してください。

tall 配列の使用

tall 配列は、大きすぎてメモリに収まらないデータ セットの操作に役立ちます。MATLAB では、データを小さなブロックごとに操作し、データのチャンク化や処理はすべてバックグラウンドで自動処理されます。tall 配列は主に 2 つの方法で使用できます。

  • メモリには収まるものの、計算を実行しようとするとメモリ不足となるような大規模な配列がある場合は、その配列を tall 配列にキャストできます。

    t = tall(A);
    この方法により、メモリには収まるものの、メモリ消費量が多すぎて計算中にデータのコピーができないような、大規模な配列を扱えるようになります。たとえば、8 GB の RAM と 5 GB の行列がある場合、この行列を tall 配列にキャストすると、メモリ不足を発生させずに行列の計算を実行できます。この使用法の例については、tall を参照してください。

  • ファイルまたはフォルダーベースのデータがある場合は、データストアを作成してから、そのデータストアに基づく tall 配列を作成できます。

    ds = datastore("path/to/file.csv");
    t = tall(ds);
    この方法により、MATLAB の tall 配列を最大限に活用できます。データは任意の行数をもつことができ、MATLAB でメモリ不足は発生しません。また、datastore はローカルとリモート両方のデータの場所で機能するため、扱うデータを解析するために、そのデータが使用中のコンピューター上にある必要はありません。詳細については、リモート データの操作を参照してください。

tall 配列の詳細については、メモリに収まらないデータの tall 配列を参照してください。

複数のマシンのメモリの使用

コンピューター クラスターを使用している場合は、分散配列 (Parallel Computing Toolbox™ が必要) を使用し、クラスター内のすべてのマシンのメモリを結合して計算を実行できます。データがメモリに収まるかどうかに応じて、異なる方法でデータを並列プールのワーカー間で分割できます。詳細については、並列ワーカーへの配列の分散 (Parallel Computing Toolbox)を参照してください。

設定と基本設定の調整

一般に、コードを書き換えると最も効果的にメモリ パフォーマンスを向上できます。ただし、コードを変更できない場合は、次の解決策によって必要な量のメモリが得られる可能性があります。

Java Virtual Machine を使用しない MATLAB の起動または Java ヒープ サイズの縮小.  Java® Virtual Machine (JVM™) ソフトウェアを使用せずに MATLAB を起動するか、Java ヒープ サイズを減らすと、使用可能なワークスペース メモリを増やすことができます。JVM を使用せずに MATLAB を起動するには、コマンド ライン オプション -nojvm を使用します。Java ヒープ サイズを減らす方法については、Java ヒープ メモリ基本設定を参照してください。

-nojvm を使用すると、デスクトップ ツールやグラフィックスなど、JVM に依存するいくつかの機能が失われるという不利な点があります。-nodesktop オプションを使用して MATLAB を起動しても、あまりメモリの節約にはなりません。

配列サイズの制限の調整.  配列サイズが配列の最大サイズの基本設定を超えていることを示すエラーが発生した場合は、MATLAB でこの配列サイズの制限を調整できます。配列サイズの制限の調整については、ワークスペースと変数の基本設定を参照してください。この解決策は、作成する配列が現在の配列サイズの上限を超えているが、大きすぎずメモリに収まる場合にのみ役立ちます。配列サイズの制限をオフにしても、大きすぎる配列の作成の試行によって MATLAB がメモリ不足になるか、過度のメモリ ページングが原因で MATLAB またはコンピューターが応答しなくなる可能性があります。

参考

| | |

関連するトピック