雲雀は高く空を舞い このページをアンテナに追加 RSSフィード

「ひよこは高く空を舞い」について

2007-02-12

目次

  • [Matlab]matlabパフォーマンス改良の手法について
  • パフォーマンス改良の手法
    • ユーザのプログラムのパフォーマンスの解析
    • ループのベクトル化
    • 配列の前もってメモリを割り当てる
      • 倍精度でない行列の前もっての設定
      • 配列を拡大する必要がある場合に、repmat を使用
    • 高速化する他の方法
      • 高速化のために、ループをMEX-ファイルでコード化
      • ファンクションは、スクリプトより速い
      • load と save は、ファイルI/O関数より高速です。
      • 大きなバックグラウンドプロセスを避ける
  • パフォーマンスの高速化
    • MATLAB の高速化とは
    • 配列の型
    • ループに対して
    • 条件文
    • 配列サイズ
    • MATLAB が高速化できないもの
      • データタイプと配列の型
      • 関数コール
      • MATLAB 組み込み関数をオーバロードした関数:
      • ライン上に複数の演算が存在する場合
      • データタイプ、または、変数の型の変更
    • ページのファイルでのベクトル化と前もってのメモリの割り当て法
      • ループのベクトル化
      • 前もって、配列を割り当てる
      • MATLAB実行中に避けること
  • 高速化のサンプル
  • メモリの効率的な使用法
    • メモリ管理関数
    • 保護したメモリの使用
      • 変数の取り扱い
      • メモリ内でデータの圧縮
      • セル配列に対して必要なメモリ
      • 配列の構造体と構造体の配列との比較
      • 入れ子になった関数セル
  • 「MATLAB Programming(R2006b)」より
  • メモリの効率的な利用
    • 倍精度でない行列の事前設定
    • MEX-ファイル内のコーディングループ
  • 実数データの演算
    • 適当な論理演算子の使用
  • 配列に対するメモリの割り当て
    • 配列ヘッダ
    • 構造体とセル配列.
    • 関数引数
    • 大きなデータセットの取り扱い.
  • データ構造とメモリ
    • 数値配列
    • 符号付および符号なし整数
    • 浮動小数点数
    • 複素数配列
    • スパース行列
    • 大きな行列を予め割り当てる

パフォーマンス改良の手法  パフォーマンス改良の手法 - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  パフォーマンス改良の手法 - 雲雀は高く空を舞い  パフォーマンス改良の手法 - 雲雀は高く空を舞い のブックマークコメント

ユーザのプログラムのパフォーマンスの解析

    • Profile
    • tic/toc

ループのベクトル

配列の前もってメモリを割り当てる

倍精度でない行列の前もっての設定

double でないタイプの行列をホールドすることで、メモリブロックを前もって割り当てる場合、repmat を使うよりも、よりメモリ効率が良くなり、しばしば高速になります。

下のステートメントは、zeros を使って、uint8 の100行100列の前もって設定します。まず、double のフル行列を作成し、uint8 行列に変換します。これは、不必要に時間とメモリを使います。

A = int8(zeros(100));

repmat を使って、double のものを一つだけ作成します。それで、必要になるメモリを減らします。

A = repmat(int8(0), 100, 100);
配列を拡大する必要がある場合に、repmat を使用

repmat は、拡張する配列に対して、メモリの連続するブロックを確保しようとします。

高速化する他の方法

高速化のために、ループをMEX-ファイルでコード化
ファンクションは、スクリプトより速い

スクリプトが、MATLAB で使われる場合はいつも、メモリにロードされ、一度に一つのラインが処理されます。一方、ファンクションは、擬似コードにコンパイルされ、メモリにロードされます。

load と save は、ファイルI/O関数より高速です。
大きなバックグラウンドプロセスを避ける

パフォーマンスの高速化  パフォーマンスの高速化 - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  パフォーマンスの高速化 - 雲雀は高く空を舞い  パフォーマンスの高速化 - 雲雀は高く空を舞い のブックマークコメント

MATLAB の高速化とは

配列の型

高速化は、3次元より高い次元をもつ配列を除いて、すべてのMATLAB 配列型に適用されます。

ループに対して

for ステートメントによりコントロールされるループは、MATLAB の中で、つぎの場合に、より高速に実行されます。

  • for ループに対するインデックスが、スカラ値の範囲で設定されている。
  • for ループに中のコードが、サポートしているデータタイプや配列の型にのみ使われている。
  • forループの中のコードが、いくつかの関数をコールしている場合、これらが、組み込み関数である。

ループパフォーマンスは、ループの中のすべてのコードが、高速化に対して、必要条件を満たしている場合に、最適化されます。このような場合、MATLAB は、for や end ステートメントを含む、すべてのループの実行をスピードアップします。

条件文

if, elseif, while, switch ステートメントは、ステートメントの中で条件文がスカラ値を計算する場合に限り、高速に実行します。

配列サイズ

配列を取り扱うとき、MATLAB が実行するために必要なオーバヘッド量があります。大きい配列に対して、この量は、データを取り扱うために必要となる時間と比べると小さいものになります。しかし、小さい行列に対して、オーバヘッドは、割合が大きくなり、より意味をもつものになります。

高速化は、配列を取り扱うために必要なオーバヘッドの量を小さくすることになります。従って、より小さい配列を使ったプログラムを記述することで、スピードアップが図られます。

MATLAB が高速化できないもの

データタイプと配列の型

ある種のデータタイプ、3次元以上の配列も高速化されていません。

関数コール

他の関数(M-ファイル、または、MEX-ファイル)、または、サブ関数のコールは、高速化と言う表現では意味が少ないもので、MATLAB の中では、高速化に寄与しません。コールされた関数を作っているコードは、高速化としては改良されていますが、セルを構成するメカニズム的には、効果が少なくなるものです。

MATLAB 組み込み関数をオーバロードした関数:

オーバロードされた関数の実行は、M-ファイル、または、MEX-ファイルをコールする関数を含んでいるので、MATLAB の組み込み関数をオーバロードすることは、実行速度を落とすことになります。たとえば、タイプ char に対して、組み込み関数 eq をオーバロードする場合、等価なキャラクタとの比較は、高速化をサポートしている MATLAB の組み込みの eq を使うときに比べ遅くなります。

ライン上に複数の演算が存在する場合

ある環境の基で、ユーザのプログラムの中の一つのラインに複数の演算を含んでいる場合、速度は遅くなります。ここで示される例題の中で、最初の演算は、構造体配列を含み、高速化でサポートされていないものです。

x = a.name;   for k=1:10000, sin(A(k)), end;

MATLAB は、同時にすべてのラインを、連続的にコードを処理しているので、同じライン上に効率の良くない演算が存在すると高速化は行なわれません。これは、この例題の中で、for ループが、別々のライン上にある場合に得る高速化の恩恵を受けていないことを意味します。

データタイプ、または、変数の型の変更

ユーザのプログラムが、存在している変数のデータタイプ、または、配列の型を変更する場合、 MATLAB は、一時的に、そのコードの高速化プロセスを停止し、変更を処理します。

ページのファイルでのベクトル化と前もってのメモリの割り当て法

ループのベクトル
前もって、配列を割り当てる
MATLAB実行中に避けること

メモリの効率的な使用法  メモリの効率的な使用法 - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  メモリの効率的な使用法 - 雲雀は高く空を舞い  メモリの効率的な使用法 - 雲雀は高く空を舞い のブックマークコメント

メモリ管理関数

  • whos, pack, clear, save, load, quit

保護したメモリの使用

変数の取り扱い

変数を作成する場合、メモリを保護するには、

  • 大きなテンポラリの変数を作成することを避け、必要なくなった場合、すぐにテンポラリ変数を削除します。
  • 固定したサイズの配列を取り扱う場合、大きくする必要が生じる度に、MATLAB でリサイズしないで、前もって割り当てておきます。
  • 空行列に等しい変数をフリーメモリに設定するか、または、<clear variable_name>を使って、クリアします。
  • できる限り、変数を再利用します。
メモリ内でデータの圧縮

MATLAB は、メモリ管理をヒープ方式で行なっているので、拡張したMATLAB セッションは、メモリを分断する原因になります。メモリが分断された場合、フリースペースの部分が多くなりますが、新しい大きな変数をストアするには、連続したフリーのスペースが十分でない場合が生じます。MATLAB から、Out of Memory メッセージが表示されると、pack 関数は、メモリ内のユーザのデータのいくつかを圧縮し、連続したフリーな空間を作成します。

セル配列に対して必要なメモリ

連続したフリーのメモリは、セル配列全体に対しては必要ありません。セル配列は、他の配列へのポインタの配列なので、個々の配列に対するメモリは、連続している必要がありますが、全体のメモリの収集には、連続している必要はありません。

配列の構造体と構造体の配列との比較

配列の構造体は、構造体の配列より、保存するためには、多くのメモリを必要としません。また、配列の構造体を使うことは、スピードの面でも利点を示します。

たとえば、つぎの配列の構造体を作成しましょう。

struct_of_array.A = magic(100);
struct_of_array.B = rand(100);

また、同じ次元をもち、配列の構造体と同じデータをもつ構造体の配列を作ります。

for m = 1:100
    for n = 1:100
        array_of_struct(m,n).A = struct_of_array.A(m,n);
        array_of_struct(m,n).B = struct_of_array.B(m,n);
    end
end

2つのメモリの使用量を比べて見ましょう。そして、構造体の配列は、同じデータを含む配列の構造体に対して、約8.5 倍のメモリを必要としています。

NameSizeBytesClass
array_of_struct100x1001360128struct array
struct_of_array1x1 160248struct array
入れ子になった関数セル

入れ子になっている関数で使われるメモリの量は、連結したライン上でこれらをコールして使う量の総量と同じです。つぎの2つの例題は、同じ量のメモリを必要とします。

result = function2(function1(input99));
result = function1(input99);
result = function2(result);


メモリの効率的な利用  メモリの効率的な利用 - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  メモリの効率的な利用 - 雲雀は高く空を舞い  メモリの効率的な利用 - 雲雀は高く空を舞い のブックマークコメント

倍精度でない行列の事前設定

(1) A = int8(zeros(100));
(2) A = zeros(100, 'int8');

(1)のステートメントに対して(2)のほうが効率的。(1)のステートメントは、まず完全な double の行列を作成することによって int8 の 100×100 の行列を事前割り当てし、各要素を int8 配列に変換します。 これでは時間もメモリも不必要に浪費してしまいます。

MEX-ファイル内のコーディングループ

for または whileループを使う必要がある場合、MEX-ファイルでループをコード化します。この方法により、ループ内の命令を実行するたびに翻訳する必要がなくなるため、ループの実行速度は上がります。

External Interfaces ドキュメントの "Introducing MEX-Files" を参照してください。

実数データの演算  実数データの演算 - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  実数データの演算 - 雲雀は高く空を舞い  実数データの演算 - 雲雀は高く空を舞い のブックマークコメント

実数 (すなわち、 非複素数) の演算を行う場合、 実数に対して特別に設計された MATLAB 関数の使用がより効率的です。 つぎの関数は、実数である数値を返します。

  • reallog
    • 非負実数配列に対する自然対数の検出
  • realpow
    • 実数のみの出力に対する配列のベキの検出
  • realsqrt
    • 非負実数配列に対する平方根の検出

適当な論理演算子の使用

論理演算子AND または OR を実行する場合、それぞれのタイプの 2 つの演算子を選択します。

  • &, |
    • 要素毎に論理演算子 AND および OR を配列に実行する
  • &&, ||
    • ショートサーキットを使用して、論理演算子 AND および OR をスカラ値に実行する

if と whileステートメントにおいて、ショートサーキット演算子として、論理 AND に対して&& 、 論理 OR に対して、||を使用することがより効率的です。なぜなら、これらの演算子は、多くの場合、論理式全体を評価する必要がないためです。たとえば、 MATLAB は、入力引数の数が 3 より小さい場合、この式の最初の部分のみを評価します。

if (nargin >= 3) && (ischar(varargin{3}))



配列に対するメモリの割り当て  配列に対するメモリの割り当て - 雲雀は高く空を舞い を含むブックマーク はてなブックマーク -  配列に対するメモリの割り当て - 雲雀は高く空を舞い  配列に対するメモリの割り当て - 雲雀は高く空を舞い のブックマークコメント

配列ヘッダ

変数に配列を割り当てる場合、MATLAB も、 ヘッダと呼ばれるメモリの断片に (データタイプや次元など) 配列についての情報をストアします。たいていの配列に対して、ヘッダをストアするために必要なメモリは、 大きくはありません。 大きなデータセットを少数の大きな配列にストアすることは、多数の小さな配列にストアすることに比べ、必要な 配列ヘッダがより少ないため、利点は少なくなります。

構造体とセル配列.

構造体とセル配列に対して、MATLAB は、各配列に対するのみでなく、 構造体の各フィールド、セル配列の各セルに対してもヘッダを作成します。このため、構造体またはセル配列をストアするために必要なメモリ量は、データの保持量のみならず、作成される方法にも依存します。 

たとえば、 フィールド R, G, B を持つ、スカラ構造体配列 S1、大きさ 100×50 の各フィールドは、 構造体全体を記述するために、 1 つの配列ヘッダ、 3 つのフィールド配列のぞれぞれを記述する 1 つずつのヘッダが必要であり、データ構造全体に対して、合計 4 つの配列ヘッダを作成します。

S1.R(1:100,1:50)
S1.G(1:100,1:50)
S1.B(1:100,1:50)

他方、 各要素がスカラフィールド R, G, B である 100×50 構造体配列 S2 は、構造体全体を記述する1つの配列ヘッダと、 構造体の 5,000要素の各々に対して、フィールド毎に 1 つの配列ヘッダを必要とし、 データ構造全体に対して、15,001 の配列ヘッダを作成します。

S2(1:100,1:50).R
S2(1:100,1:50).G
S2(1:100,1:50).B

こうして、S1 と S2 は、同じ量のデータを含みますが、 S1 は、かなり少ないメモリスペースを使用します。 より少ないメモリが要求されるだけでなく、同様に、 S1 のフォーマットを使用することにより高速化されます。


関数引数

MATLAB は、関数呼び出しで渡される引数を同様に取り扱います。関数に 1 つの変数を渡す場合、実際には、変数が表すデータに対するポインタも渡しています。入力データが呼び出される関数により修正されない限り、呼び出す関数内の変数と、呼び出された関数内の変数は、メモリ内の同じ位置を指します。呼び出された関数が入力データの値を修正する場合、 MATLAB は、メモリ内の新しい位置に、オリジナルの配列のコピーを作成します。 そのコピーを修正された値を使用して更新し、呼び出された関数内の入力変数をこの新しい配列に対してポイントします。

下記の例題では、 関数 myfun は、関数に渡された配列の値を修正します。 MATLAB は、 Aによりポイントされる配列のコピーをメモリに作成し、変数 X をこの新しい配列へのポインタとして設定し、その後、 X の1行をゼロに設定します。 A により参照される配列は、変更されません。

A = magic(500);
myfun(A);

function myfun(X)
X(400,:) = 0;

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

再度、大きなデータセットを取り扱う場合、呼び出された関数が値を修正する時に、 MATLAB が Aの一時的なコピーを作成することに注意する必要があります。 この ため、配列のストアに必要なメモリが倍になります。これは、十分なメモリを利用できない場合、 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の様々なデータ構造で異なります。MATLABのストア方法を考慮することにより、これらの構造に使用されるメモリ量を減らすことができる可能性があります。

数値配列

符号付および符号なし整数

8-bits1-byte16-bits2-bytes32-bits4-bytes64-bits8-bytes

浮動小数点数

single4-bytesdouble8-bytes

複素数配列

MATLAB は、複素数データを実部と虚部として別にストアします。複素配列変数のコピーを作成し、 その後、配列の実部または虚部のみ修正する場合、 MATLAB は、実部と虚部の両方を含む新しい配列を作成します。

スパース行列

スパース形式で大部分ゼロであるような値を持つ行列をストアすることが最良です。スパース行列は、より少ないメモリを使用し、フル行列の操作よりも高速になります。 sparse 関数を使用して、フル行列をスパース形式に変換することができます。

大きな行列を予め割り当てる

MATLAB では、カレントの変数を保存するために、MATLAB ヒープの中で利用可能なメモリが足りない場合、オペレーティングシステムのメモリを要求します。必要なメモリセグメントのサイズがMATLAB ヒープで利用可能な限り、ヒープの中でメモリを再使用します。

たとえば 1 台のマシン上で、つぎのステートメント群が約 15.4 MBの RAM を使うとします。 

a = rand(1e6,1);
b = rand(1e6,1);

つぎのステートメントは、約 16.4 MB の RAM を使います。

c = rand(2.1e6,1);

つぎのステートメント群は、約 32.4 MB の RAM を使います。 これは、MATLAB では 2 つの 1 MB の配列に占領されていた空間に 2.1MB の配列を入れることができないためです。

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

メモリの過剰な割り当てを防ぐ最も簡単な方法は、最初に大きなベクトルを割り当てることです。 つぎのステートメント群は、約 16.4 MB の RAM のみを使います。

c = rand(2.1e6,1);
clear
a = rand(1e6,1);
b = rand(1e6,1);
トラックバック - http://chick.g.hatena.ne.jp/allegro/20070212
テクノラティプロフィール