ドキュメンテーション センター

  • 評価版
  • 製品アップデート

最新のリリースでは、このページがまだ翻訳されていません。 このページの最新版は英語でご覧になれます。

メモリを効率的に使用するための対策

必要なメモリ量を減らす方法

メモリ不足の問題は、多くの場合、ファイルまたはデータベース内などにある既存の大規模なデータ セットの解析や処理が原因で発生します。このような場合には、MATLAB® ソフトウェア プロセスに、データ セットのすべてまたは一部を取り込むことが必要となります。次の手法では、この段階で必要となるメモリを最小にします。

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

問題を解くには、大規模なデータ セットから必要な部分のみを MATLAB にインポートするようにします。クエリに一致する要素を明示的に検索できるデータベースなどのソースからインポートする場合は、こうした問題はありません。ただし、大規模なテキスト ファイルやバイナリ ファイルを読み込む場合には共通する問題となります。ファイル全体を読み込む代わりに、適切な MATLAB 関数を使用して、ファイルの一部のみを読み込むようにします。

MAT ファイル-  関数 matfile で作成するオブジェクト内にインデックスを付けることにより、変数の部分を読み込みます。

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

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

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

ブロックごとにデータを処理する

ブロック処理、つまり大規模なデータ セットを 1 ループで一度に 1 セクションごとに処理することを考えます。データ セット内の最大の配列のサイズを小さくすると、必要なコピーまたは一時変数のサイズも減少します。この手法は次の 2 とおりの方法のいずれかで使用できます。

  • 分割して独立に処理できるアプリケーションのサブセットの場合

  • フィルター処理など、以前のブロックの状態にのみ依存するアプリケーションの場合

一時的な配列の作成の回避

大きな一時変数の作成を回避し、一時変数が不必要になった際にはクリアするようにします。たとえば、0 で構成される大規模な配列を作成する際には、一時変数 A に保存して、A を single に変換する代わりに、次のようにします。

A = zeros(1e6,1);
As = single(A);

両方の操作を 1 つのコマンドだけで行います。

A = zeros(1e6,1,'single');

関数 repmat、配列の事前割り当て、および for ループを使用しても、メモリ内で一時的なストレージを使用せずに nondouble データを操作できます。

入力引数を減らすための入れ子関数の使用

大きなデータ セットを扱う場合には、呼び出された関数で値を変更する際に、MATLAB によって入力変数の一時的なコピーが作成されることに注意しなければなりません。このため、配列の格納に必要なメモリが 2 倍になり、十分なメモリがないと MATLAB でエラーが発生します。

このような状況で使用メモリを減らす方法の 1 つとして、入れ子関数を使用することが考えられます。入れ子関数は外側の関数すべてとワークスペースを共有するため、通常のスコープ外にあるデータにもアクセスできます。ここで示す例では、入れ子関数 setrowval は、外側の関数 myfun のワークスペースに直接アクセスでき、関数呼び出しにおいて変数のコピーを渡す必要はありません。setrowval によって A の値が変更される際には、呼び出し側の関数のワークスペース内で変更されます。呼び出す関数のために個別の配列を保持するための追加メモリは必要なくA の変更された値を返す必要もありません。

function myfun
A = magic(500);

   function setrowval(row, value)
   A(row,:) = value;
   end

setrowval(400, 0);
disp('The new value of A(399:401,1:10) is')
A(399:401,1:10)
end

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

MATLAB では、doubleuint8 などの、異なるサイズのデータ クラスが提供されています。このため、小さなデータ セグメントを格納するために大きなクラスを使用する必要はありません。たとえば、1,000 個の小さな符号なし整数値を格納するには、uint8 クラスを使用すれば、double クラスの場合よりも必要なメモリが 7 KB 少なくて済みます。

適切な数値クラスの使用

MATLAB では、操作の内容の応じて異なる数値クラスを使用できます。既定のクラス double では精度が最良となりますが、格納にはメモリの要素ごとに 8 バイトが必要です。線形代数のように複雑な計算を行う場合は、doublesingle などの浮動小数点クラスを使用しなければなりません。single クラスで必要なのは、4 バイトのみです。singles で実行できる操作にはいくつかの制約がありますが、ほとんどの MATLAB 数学演算がサポートされています。

簡単な演算のみを実行し、元のデータを整数として表す場合は、MATLAB の整数クラスを使用できます。以下に、数値クラス、必要なメモリ (バイト単位)、サポートされる演算を一覧にします。

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

データを格納する際のオーバーヘッドの削減

mxArrays として内部実装される MATLAB 配列では、タイプ、次元、属性など、データに関するメタ情報を保存するメモリ スペースが必要です。これには、配列ごとに約 80 バイト必要です。このオーバーヘッドは、多数の (何百または何千の) 小さな mxArrays (スカラー) がある場合には問題になります。whos コマンドでは変数で使用されるメモリが一覧表示されますが、このオーバーヘッドは含まれません。

1 つの mxArray で構成される簡単な数値配列ではオーバーヘッドが最小になるので、できるだけこの数値配列を使用するようにします。データが複雑すぎて単純な配列や行列に保存できない場合は、他のデータ構造を使用できます。

セル配列は、各要素に対する別々の mxArrays で構成されます。このため、多くの小さな要素をもつセル配列ではオーバーヘッドが大きくなります。

構造体では、フィールドごとにほぼ同量のオーバーヘッドが必要です (上記の「配列ヘッダー」のドキュメンテーションを参照してください)。多くのフィールドと小さなコンテンツをもつ構造体は、オーバーヘッドが大きくなるので避けるようにします。数値スカラー フィールドをもつ構造体の大規模な配列では、大規模な数値配列を含むフィールドをもつ構造体よりも、多くのメモリが必要です。

また、MATLAB では数値配列が連続したメモリに格納されますが、構造体やセル配列ではメモリは連続していないことに注意してください。

適切な MATLAB クラスへのデータのインポート

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

fid = fopen('large_file_of_uint8s.bin', 'r'); 
a = fread(fid, 1e3, 'uint8');              % Requires 8k 
whos a
  Name         Size            Bytes  Class    Attributes
 
  a         1000x1              8000  double    
  
a = fread(fid, 1e3, 'uint8=>uint8');       % Requires 1k 
whos a
  Name         Size            Bytes  Class    Attributes
 
  a         1000x1              1000  uint8

可能であれば配列をスパースにする

データに多くのゼロが含まれる場合は、ゼロ以外の要素のみを格納する、スパース配列の使用を考慮します。次の例では、主に 0 で構成される配列のストレージに必要なスペースを比較します。

A = diag(1e3,1e3);    % Full matrix with ones on the diagonal
As = sparse(A)        % Sparse matrix with only nonzero elements
whos
  Name      Size                  Bytes  Class
 
  A      1001x1001                8016008  double array
  As     1001x1001                4020     double array (sparse)

この配列は、スパースとして保存した場合は約 4 KB 必要ですが、非スパース行列では約 8 MB になることがわかります。一般に、ゼロ以外の要素数が nnz 個、列数が ncol の double 型スパース配列で必要なメモリは以下のとおりです。

  • 16 * nnz + 8 * ncol + 8 バイト (64 ビットのコンピューターの場合)

  • 12 * nnz + 4 * ncol + 4 バイト (32 ビットのコンピューターの場合)

MATLAB では、スパース配列では一部の数学演算がサポートされないことに注意してください。

メモリの断片化を回避する方法

MATLAB では、数値配列を格納する場合には、常にメモリの連続したセグメントが使用されます。ただし、このデータを操作すると、連続的なブロックが断片化することがあります。メモリが断片化されると、空き容量はたくさんあっても、大きな変数を新しく格納するために十分な連続したメモリがないという状態になります。断片化が進むと、必要以上のメモリが使用されるようになります。

配列作成時における連続メモリの事前割り当て

MATLAB セッションでは、動的なメモリの割り当てと解放によって、メモリが断片化することがあります。for ループと while ループでは繰り返しのたびにデータ構造のサイズが "増大" し、データを格納するために前回よりも大きなメモリ ブロックが繰り返し検出および割り当てられるため、断片化が進むことがあります。

メモリをより効率的に使用するには、ループに入る前に、最終的なサイズの行列を保持するのに十分なサイズのメモリ ブロックを事前に割り当てるようにします。配列にメモリを事前に割り当てると、計算の始めに MATLAB によって、フルサイズの配列全体に十分な量の連続したスペースが確保されます。このスペースが確保されると、メモリ内で新しい連続したスペースを繰り返し割り当てなくても、配列に要素を追加できます。

詳細は、「配列の事前割り当て」を参照してください。

大きな配列から割り当て

MATLAB では、メモリ管理にヒープ法が使用されます。ヒープ内に現在の変数を保存するために十分なメモリがない場合は、オペレーティング システムのメモリが要求されます。ヒープ内で必要なサイズのメモリ セグメントが使用可能であれば、ヒープ内のメモリが再使用されます。

以下のステートメントでは、約 4.3 MB の RAM が必要です。これは、MATLAB で 2.3 MB の配列に領域を割り当てる際に、以前に 2 つの 1 MB の配列で占められていた領域を再使用できない場合があるからです。

a = rand(1e6,1);
b = rand(1e6,1);
clear
c = rand(2.3e6,1);

メモリの過剰な割り当てを防ぐ最も簡単な方法は、最初に大きなベクトルを割り当てることです。以下のステートメントでは、必要な RAM が約 2.0 MB になります。

c = rand(2.3e6,1);
clear
a = rand(1e6,1);
b = rand(1e6,1);

長期の使用 (Windows システムの場合)

32 ビットの Microsoft® Windows® では、Windows のメモリ マネージャーによって特定のタイプとサイズのブロックがオペレーティング システムに返されないため、MATLAB のワークスペースは時間が経つと断片化します。MATLAB ワークスペースをクリアしても、この問題は解決されません。最初に最大の変数を割り当てることによって、この問題を最小に抑えることができます。ただしこの方法では、MATLAB を何日も何週間も連続して使用した場合などに発生する、ワークスペースの断片化には対処できません。このような場合の唯一の解決策は、作業を保存して MATLAB を再起動することです。

pack コマンドは、すべての変数をディスクに保存してから読み込むので、この状況では役立ちません。

使用されたメモリの再使用

使用可能なメモリの量を簡単に増やす方法の 1 つとして、使用していない大きな配列をクリアすることがあげられます。

大きなデータの定期的なディスクへの保存

プログラムで大量のデータが生成される場合、データを定期的にディスクに書き込むようにします。この部分のデータを保存した後に、メモリから変数を clear して、データ生成を続行します。

不要な変数のメモリからのクリア

非常に大規模なデータ セットを繰り返し、または対話形式で扱う場は合、まず古い変数をクリアして、新しい変数のための容量を確保します。この作業を行わない場合、変数をオーバーライドにする前に、MATLAB で同じサイズの一時的なストレージが必要になります。以下に例を示します。

a = rand(100e6,1)              % 800 MB array b = rand(100e6,1)              % 新しい 800 MB の配列。rand を使用中のエラー。メモリ不足です。オプションを参照する場合は、HELP MEMORY と入力します。
 
a = rand(100e6,1)              % 新しい 800 MB の配列をクリア 
この情報は役に立ちましたか?