Main Content

MATLAB でのメモリの割り当て方法

このトピックでは、変数を扱うときに、MATLAB® がメモリを割り当てる方法について説明します。こうした情報 (MATLAB によるデータの内部的な処理に関する情報など) は、将来のリリースで変更されることがあります。

配列へのメモリの割り当て

数値配列や文字配列を変数に代入する場合、MATLAB では、メモリの連続したブロックを割り当て、そのブロックに配列データを保存します。また、MATLAB では、クラスや次元などの配列データに関する情報も "ヘッダー" と呼ばれる別の小さなメモリ ブロックに保存されます。ほとんどの配列では、ヘッダーを保存するために必要なメモリは大きくありません。ただし、大規模なデータ セットを、多数の小さな配列ではなく、少数の大きな配列に格納する方が、利点がある可能性があります。これは、配列の数が少なければ、必要な配列ヘッダーの数も少ないからです。

既存の配列に新しい要素を追加すると、格納領域を連続に保つよう、MATLAB によってメモリ内の配列が拡張されます。通常、これには、拡張後の配列を保持するために十分な大きさの新しいメモリ ブロックを見つけることが必要になります。MATLAB によって、配列の内容が元の位置から新しいメモリ ブロックにコピーされ、このブロックの配列に新しい要素が追加され、メモリの元の配列の位置が解放されます。

既存の配列から要素を削除すると、MATLAB では、削除した要素が除去されてメモリの格納領域が連続に保たれ、元のメモリの位置で格納領域が圧縮されます。

配列のコピー

2 番目の変数に配列を割り当てる場合 (たとえば、B = A を実行する場合)、MATLAB はすぐに新しいメモリを割り当てることはありません。代わりに、配列参照のコピーを作成します。A および B によって参照されているメモリ ブロックの内容を変更しない限り、データの複数のコピーを保存する必要はありません。ただし、A または B のいずれかを使用してメモリ ブロックの任意の要素を変更した場合、MATLAB は新しいメモリを割り当て、データをそれにコピーし、作成されたコピーを変更します。

Windows® システムでは、関数memoryを使用することでメモリの詳細を確認できます。配列のコピーが Windows システムでのメモリ使用量にどのように影響するかを確認するために、現在のフォルダー内のファイルに関数 memUsed を作成します。この関数は、memory を呼び出して、MATLAB プロセスで使用されるメモリ量をメガバイト単位で返します。

function y = memUsed
usr = memory;
y = usr.MemUsedMATLAB/1e6;

memUsed を呼び出して、現在のメモリ使用量を表示します。

format shortG
memUsed
ans = 
       3966.1

2,000 行 2,000 列の数値配列を作成し、メモリ使用量の変化を観察します。この配列は約 32 MB のメモリを使用します。

A = magic(2000);
memUsed
ans = 
       3998.1

BA のコピーを作成します。配列データのコピーが 2 つある必要はないので、MATLAB は配列参照のコピーのみを作成します。このため、メモリの使用量が大幅に増加することはありません。

B = A;
memUsed
ans = 
       3998.1

ここで B に、行の半分を削除する変更を行います。AB は同じデータを指さなくなるため、MATLAB は別のメモリ ブロックを B に割り当てなければなりません。その結果、MATLAB プロセスによって使用されるメモリ量は、B のサイズである約 16 MB 増加します (A に必要な 32 MB の半分)。

B(1001:2000,:) = [];
memUsed
ans = 
       4014.1

関数の引数

MATLAB では、関数呼び出しで渡される引数は、コピーされている配列を処理する場合と同様に扱われます。関数に 1 つの変数を渡すときには、実際は、変数が表すデータに対する参照を渡します。呼び出された関数でデータが変更されない限り、呼び出し側の関数またはスクリプト内の変数と、呼び出された関数内の変数は、メモリ内の同じ位置を指しています。呼び出された関数で入力データの値が変更される場合は、MATLAB によってメモリ内の新しい位置に元の変数のコピーが作成され、このコピーが変更された値で更新され、呼び出された関数内の入力引数がこの新しい場所を指すようになります。

たとえば、渡される配列の値を変更する関数 myfun について考えます。MATLAB によって、メモリ内の新しい場所に A のコピーが作成され、変数 X がこのコピーへの参照に設定され、X の 1 行がゼロに設定されます。A により参照される配列は、変更されません。

A = magic(5);
myfun(A)

function myfun(X)
X(4,:) = 0;
disp(X)
end

呼び出し側の関数またはスクリプトで myfun に渡した配列の変更された値が必要な場合は、呼び出された関数の出力として更新された配列を返さなければなりません。

データ型とメモリ

メモリ要件は MATLAB データ型によって異なります。MATLAB でのさまざまなデータ型の処理方法を学習することで、コードによって使用されるメモリ量を減らせる場合があります。

数値配列

MATLAB では、8 ビット、16 ビット、32 ビット、64 ビットの符号付きおよび符号なし整数に対して、それぞれ、1、2、4、8 バイトを割り当てます。これは、浮動小数点数の倍精度 (double) または単精度 (single) のいずれかの形式を表しています。MATLAB では、4 バイトで single 型の数値が保存されるので、8 バイトを使用する double 型の数値より必要なメモリが少なくなります。ただし、single 型の数値は、保存する場合の使用ビットが少ないので、double 型の数字より精度が低くなります。MATLAB では、double は既定の数値データ型であり、ほとんどの計算タスクで十分な精度を提供します。詳細については、浮動小数点数を参照してください。

構造体と cell 配列

数値配列は連続したメモリのブロックに保存しなければなりませんが、構造体と cell 配列は不連続なブロックに保存できます。構造体と cell 配列について、MATLAB では配列に対してのみでなく、構造体の各フィールド、cell 配列の各セルに対してもヘッダーが作成されます。このため、構造体または cell 配列を格納するために必要なメモリ量は、保存するデータ量だけでなく作成方法にも依存します。

たとえば、フィールド RG、および B をもつスカラー構造体 S1 について考えます。各フィールドには 100 行 50 列の配列が含まれています。S1 では、構造体全体を記述するためのヘッダーが 1 つ、一意のフィールド名ごとにヘッダーが 1 つ、フィールドごとにヘッダーが 1 つ必要です。これにより、構造体全体で合計 7 つのヘッダーが作成されます。

S1.R = zeros(100,50);
S1.G = zeros(100,50);
S1.B = zeros(100,50);

一方、各要素がスカラー フィールド RGB である 100 行 50 列の構造体配列 S2 を考えてみましょう。この例では、S2 には構造体全体を記述するためのヘッダーが 1 つ、一意のフィールド名ごとにヘッダーが 1 つ、5,000 要素のフィールドごとにヘッダーが 1 つ必要で、構造体配列全体で合計 15,004 の配列ヘッダーが作成されます。

for i = 1:100
    for j=1:50
        S2(i,j).R = 0;
        S2(i,j).G = 0;
        S2(i,j).B = 0;
    end
end

関数whosを使用して、64 ビット システムの S1 および S2 に割り当てられたメモリ量を比較します。S1S2 は同じデータを保持しますが、S1 では使用するメモリがかなり少なくなります。

whos S1 S2
  Name        Size              Bytes  Class     Attributes

  S1          1x1              120504  struct              
  S2        100x50            1680192  struct              

複素数配列

MATLAB は、インターリーブされた複素数格納表現を使用します。つまり、実数部と虚数部が併せて、メモリの連続するブロックに保存されます。複素配列のコピーを作成してから、配列の実数部または虚数部のみを変更すると、MATLAB では実数部と虚数部の両方を含む配列が作成されます。メモリ内の複素数を表現する方法の詳細については、MATLAB の MEX 関数におけるインターリーブされた複素数 API のサポートを参照してください。

スパース行列

いくつかの非ゼロ要素を含む行列はスパース ストレージを使用して保存することをお勧めします。非スパース行列がもつ非ゼロ要素の数が少ない場合、行列をスパース ストレージに変換することで、通常はメモリの使用量とコードの実行時間が改善されます。非スパース行列をスパース ストレージに変換するには、関数sparseを指定します。

たとえば、行列 A を 1,000 行 1,000 列の非スパース ストレージ単位行列にします。A のスパース コピーとして B を作成します。スパース ストレージでは、同じデータで使用されるメモリ量が大幅に少なくなります。

A = eye(1000);
B = sparse(A);
whos A B
  Name         Size                Bytes  Class     Attributes

  A         1000x1000            8000000  double              
  B         1000x1000              24008  double    sparse    

大きなデータ セットの扱い

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

参考

|

関連するトピック