メモリを効率的に使用するための対策
このトピックでは、MATLAB® でメモリを効率的に使用するためのいくつかの手法を説明します。
適切なデータ ストレージの使用
MATLAB では、double
や uint8
などの、異なるサイズのデータ クラスが提供されています。このため、小さなデータ セグメントを格納するために大きなクラスを使用する必要はありません。たとえば、1,000 個の小さな符号なし整数値を格納するには、uint8
クラスを使用すれば、double
クラスの場合よりも必要なメモリが 7 KB 少なくて済みます。
適切な数値クラスの使用
MATLAB では、操作の内容の応じて異なる数値クラスを使用できます。既定のクラス double
では精度が最良となりますが、格納にはメモリの要素ごとに 8 バイトが必要です。線形代数のように複雑な計算を行う場合は、double
や single
などの浮動小数点クラスを使用しなければなりません。single
クラスで必要なのは、4 バイトのみです。single
クラスで実行できる操作にはいくつかの制約がありますが、ほとんどの MATLAB 数学演算がサポートされています。
簡単な演算のみを実行し、元のデータを整数として表す場合は、MATLAB の整数クラスを使用できます。以下に、数値クラス、必要なメモリ (バイト単位)、サポートされる演算を一覧にします。
クラス (データ型) | バイト | サポートされている演算 |
---|---|---|
single | 4 | ほとんどの数学演算 |
double | 8 | すべての数学演算 |
logical | 1 | 論理演算と条件付き演算 |
int8, uint8 | 1 | 算術関数と一部の単純な関数 |
int16, uint16 | 2 | 算術関数と一部の単純な関数 |
int32, uint32 | 4 | 算術関数と一部の単純な関数 |
int64, uint64 | 8 | 算術関数と一部の単純な関数 |
データを格納する際のオーバーヘッドの削減
mxArrays
として内部実装される MATLAB 配列では、タイプ、次元、属性など、データに関するメタ情報を保存するメモリ スペースが必要です。これには、配列ごとに約 104 バイト必要です。このオーバーヘッドは、多数の (何百または何千の) 小さな mxArrays
(スカラー) がある場合には問題になります。whos
コマンドでは変数で使用されるメモリが一覧表示されますが、このオーバーヘッドは含まれません。
1 つの mxArray
で構成される簡単な数値配列ではオーバーヘッドが最小になるので、できるだけこの数値配列を使用するようにします。データが複雑すぎて単純な配列や行列に保存できない場合は、他のデータ構造を使用できます。
cell 配列は、各要素に対する別々の mxArrays
で構成されます。このため、多くの小さな要素をもつ cell 配列ではオーバーヘッドが大きくなります。
構造体では、フィールドごとにほぼ同量のオーバーヘッドが必要です。多くのフィールドと小さなコンテンツをもつ構造体は、オーバーヘッドが大きくなるので避けるようにします。数値スカラー フィールドをもつ構造体の大規模な配列では、大規模な数値配列を含むフィールドをもつ構造体よりも、多くのメモリが必要です。
また、MATLAB では数値配列が連続したメモリに格納されますが、構造体や cell 配列ではメモリは連続していないことに注意してください。詳細については、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
可能であれば配列をスパースにする
データに多くのゼロが含まれる場合は、ゼロ以外の要素のみを格納する、スパース配列の使用を考慮します。次の例は、スパース ストレージと非スパース ストレージの要件を比較しています。
A = eye(1000); % Full matrix with ones on the diagonal As = sparse(A); % Sparse matrix with only nonzero elements whos Name Size Bytes Class Attributes A 1000x1000 8000000 double As 1000x1000 24008 double sparse
この配列は、スパースとして保存すると約 24 KB しか必要としませんが、非スパース行列ではおよそ 8 MB が必要になることがわかります。一般に、非ゼロ要素が nnz
個、列数が ncol
の double 型スパース配列で必要なメモリは以下のとおりです。
16 *
nnz
+ 8 *ncol
+ 8 バイト (64 ビットのコンピューターの場合)
MATLAB では、ほとんどの数学演算がスパース配列でサポートされていますが、全部ではありません。
データの一時コピーの回避
データの不必要な一時コピーの作成を回避することで、必要なメモリ量を大きく削減できます。
一時的な配列の作成の回避
大きな一時変数の作成を回避し、一時変数が不必要になった際にはクリアするようにします。たとえば、次のコードは一時変数 A
として保存される、0 で構成された配列を作成した後、A
を単精度に変換します。
A = zeros(1e6,1); As = single(A);
1 つのコマンドを使用して両方の演算を実行する方が、メモリ効率は高くなります。
A = zeros(1e6,1,'single');
関数 repmat
、配列の事前割り当て、および for
ループや他の方法を使用しても、メモリ内で一時的なストレージを必要とせずに double でないデータを扱うことができます。
入力引数を減らすための入れ子関数の使用
大きなデータ セットを扱う場合には、呼び出された関数で値を変更する際に、MATLAB によって入力変数の一時的なコピーが作成されることに注意しなければなりません。このため、配列の格納に必要なメモリが 2 倍になり、十分なメモリがないと MATLAB でエラーが発生します。
このような状況で使用メモリを減らす方法の 1 つとして、入れ子関数を使用することが考えられます。入れ子関数は外側の関数すべてとワークスペースを共有するため、通常のスコープ外にあるデータにもアクセスできます。ここで示す例では、入れ子関数 setrowval
は、外側の関数 myfun
のワークスペースに直接アクセスでき、関数呼び出しにおいて変数のコピーを渡す必要はありません。setrowval
によって A
の値が変更される際には、呼び出し側の関数のワークスペース内で変更されます。呼び出す関数のために個別の配列を保持するための追加メモリは必要なくA
の変更された値を返す必要もありません。
function myfun A = magic(500); setrowval(400,0) disp('The new value of A(399:401,1:10) is') A(399:401,1:10) function setrowval(row,value) A(row,:) = value; end end
使用されたメモリの再利用
使用可能なメモリの量を簡単に増やす方法の 1 つとして、使用していない大きな配列をクリアすることがあげられます。
大きなデータの定期的なディスクへの保存
プログラムで大量のデータが生成される場合、データを定期的にディスクに書き込むようにします。この部分のデータを保存した後に、メモリから変数を clear
して、データ生成を続行します。
不要な変数のメモリからのクリア
非常に大規模なデータ セットを繰り返し、または対話形式で扱う場は合、まず古い変数をクリアして、新しい変数のための容量を確保します。この作業を行わない場合、変数をオーバーライドにする前に、MATLAB で同じサイズの一時的なストレージが必要になります。以下に例を示します。
a = rand(1e5); b = rand(1e5); Out of memory. More information clear a a = rand(1e5); % New array