メインコンテンツ

MATLAB プロファイラーを使用した MEX 関数のプロファイリング

MATLAB® Coder™ によって生成された MEX 関数の実行時間を MATLAB プロファイラーを使用してプロファイリングできます。生成コードのプロファイルには、呼び出し回数および対応する MATLAB 関数の各行に要した時間が表示されます。最も時間がかかる生成コードを生み出した MATLAB コードの行を特定するには、プロファイラーを使用します。この情報は、開発サイクルの初期段階でパフォーマンスの問題を特定して修正するのに役立ちます。MATLAB プロファイラーの詳細については、profileパフォーマンス向上のためのコードのプロファイリングを参照してください。

プロファイラーへのグラフィカルなインターフェイスは MATLAB Online™ ではサポートされていません。

MEX プロファイルの生成

生成された MEX 関数を使用して、コマンド ラインで対話的に MATLAB プロファイラーを実行できます。あるいは、MATLAB 関数を呼び出すテスト ファイルがある場合は、codegen コマンドまたは MATLAB Coder アプリを使用して、MEX 関数の生成とプロファイリングを 1 ステップで実行できます。

プロファイラーが実行中の場合は、新しいプロファイルを生成する前にオフにする必要があります。プロファイラーをオフにするには、次のコマンドを使用します。

profile off;

MATLAB プロファイラーを対話的に実行

MATLAB 関数を呼び出すテスト ファイルを記述していない場合は、プロファイラーを対話的に実行する必要があります。プロファイリングに適した MEX 関数を生成するには、次のいずれかの方法を使用します。

  • コマンド ラインで MEX コード構成オブジェクトを作成します。EnableMexProfiling プロパティを true に設定します。codegen コマンドを使用してコードを生成し、構成オブジェクトを指定します。以下に例を示します。

    cfg = coder.config("mex");
    cfg.EnableMexProfiling = true;
    codegen -config cfg MyFunction

  • コマンド ラインで codegen コマンドを -profile オプション付きで使用してコードを生成します。以下に例を示します。

    codegen MyFunction -profile

  • MATLAB Coder アプリを使用する場合は、ツールストリップの [MATLAB Coder] タブで [出力タイプ]、[MEX] をクリックして、MEX 関数を生成するようにコード ジェネレーターに指示します。ツールストリップで [設定] をクリックして [コード生成設定] ダイアログ ボックスを開きます。[デバッグ] ペインで [実行プロファイリングを有効にする] チェック ボックスをオンにします。その後、ツールストリップで [コードを生成してビルド] をクリックして MEX 関数を生成します。

プロファイリングに適した MEX 関数を生成したら、次のコマンドを使用して MATLAB プロファイラーを実行し、プロファイル概要レポートを表示します。

profile on;
MyFunction_mex;
profile viewer;
profile off;

元の MATLAB ファイル MyFunction.m を変更または移動させていないことを確認します。変更または移動させていた場合、プロファイラーは MyFunction_mex のプロファイルを生成できません。

MEX 関数の生成とプロファイリングを 1 ステップで実行

MATLAB 関数を呼び出すテスト ファイルがある場合は、コマンド ラインまたは MATLAB Coder アプリを使用して、MEX 関数の生成とプロファイリングを 1 ステップで実行できます。次のいずれかの方法を使用します。

  • コマンド ラインで codegen コマンドを -test オプションおよび -profile オプション付きで使用して MEX 関数を生成します。以下に例を示します。

    codegen MyFunction -test MyFunctionTest -profile

  • ツールストリップの [MATLAB Coder] タブで [出力タイプ]、[MEX] をクリックして、MEX 関数を生成するようにコード ジェネレーターに指示します。ツールストリップで [設定] をクリックして [コード生成設定] ダイアログ ボックスを開きます。[デバッグ] ペインで [実行プロファイリングを有効にする] チェック ボックスをオンにします。ツールストリップで [生成された MEX の実行]、[実行ファイル] をクリックし、MyFunctionTest.m を選択します。

MEX の生成後、MATLAB でプロファイラー ウィンドウが開きます。

プロファイラーを使用して、最も時間がかかる生成コードを生み出す関数または MATLAB コードの行を特定します。入力行列 A および B の表現を行優先から列優先のレイアウトに 1 行で変換する MATLAB 関数の例を以下に示します。大規模な行列の場合は、こうした変換の実行に長い時間を要します。特定の行を変更して変換を回避することで、関数の効率性が向上します。

以下の MATLAB 関数について考えます。

function [y] = MyFunction(A,B) %#codegen

% Generated code uses row-major representation of matrices A and B
coder.rowMajor; 
length = size(A,1);

% Summing absolute values of all elements of A and B by traversing over the
% matrices row by row
sum_abs = 0;  
for row = 1:length 
   for col = 1:length  
       sum_abs = sum_abs + abs(A(row,col)) + abs(B(row,col));
   end
end

% Calling external C function 'foo.c' that returns the sum of all elements
% of A and B
sum = 0;
sum = coder.ceval('foo',coder.ref(A),coder.ref(B),length);

% Returning the difference of sum_abs and sum
y = sum_abs - sum;
end

この関数の生成コードでは、正方行列 A および B の行優先の表現を使用します。コードでは最初に、行列を行ごとに移動することで sum_abs (AB のすべての要素の絶対値の合計) を計算します。このアルゴリズムは、行優先のレイアウトで表現される行列向けに最適化されています。次に、コードでは coder.ceval を使用して外部の C 関数 foo.c を呼び出します。

#include <stdio.h>
#include <stdlib.h>
#include "foo.h"

double foo(double *A, double *B, double length)
{
 int i,j,s;
 double sum = 0;
 s = (int)length;
 
 /*Summing all the elements of A and B*/
 for(i=0;i<s*s;i++)
 {
         sum += A[i] + B[i];
 }
 return(sum);
}

対応する C ヘッダー ファイル foo.h は以下の通りです。

#include "rtwtypes.h"

double foo(double *A, double *B, double length);

foo.c は変数 sum を返します。この変数は、AB のすべての要素の合計です。関数 foo.c のパフォーマンスは、行列 AB が行優先のレイアウトまたは列優先のレイアウトで表現されるかどうかに影響されません。MyFunctionsum_abssum の差を返します。

大規模な入力行列 ABMyFunction のパフォーマンスを測定してから、それをさらに最適化できます。

  1. MEX プロファイリングを有効にして、MyFunction の MEX コードを生成します。2 つの大規模な乱数行列 AB に対して MyFunction_mex を実行します。プロファイル概要レポートを確認します。

    A = rand(20000);
    B = rand(20000);
    
    codegen MyFunction -args {A,B} foo.c foo.h -profile
    
    profile on; 
    MyFunction_mex(A,B);
    profile viewer;

    別のウィンドウが開き、プロファイル概要レポートが表示されます。

    Profile summary exhibiting a table with field Function Name Calls, Total Time in seconds, Self Time in seconds and total time plot. A flame graph is present, representing the table in a bar graph.

    プロファイル概要レポートには MEX ファイルとその子 (元の MATLAB 関数の生成コード) の合計時間と自己時間が表示されます。

  2. [関数名] で最初のリンクをクリックし、MyFunction の生成コードのプロファイル詳細レポートを表示します。最も時間がかかった行を確認できます。

    Table with fields Line Number, Code, Cells, Total time in seconds, Percentage of time and time plot with relevant data entries from example code. Important to point out that the total time for coder.ceval is relatively high.

  3. coder.ceval を呼び出す行に時間がかかっています (16.914 秒)。この行の実行にかなりの時間を要したのは、外部の C 関数に渡す前に、coder.ceval が行列 AB の表現を行優先のレイアウトから列優先のレイアウトに変換するためです。coder.ceval で追加の引数 -layout:rowMajor を使用することで、この変換を回避することができます。

    sum = coder.ceval('-layout:rowMajor','foo',coder.ref(A),coder.ref(B),length);
  4. 変更した MyFunction を使用して、MEX 関数とプロファイルを再度生成します。

    A = rand(20000);
    B = rand(20000);
    
    codegen MyFunction -args {A,B} foo.c foo.h -profile
    
    profile on; 
    MyFunction_mex(A,B);
    profile viewer;
    MyFunction のプロファイル詳細レポートは、coder.ceval を呼び出す行にかかる時間がわずか 0.653 秒になったことを示しています。

    Same image as mentioned above, here coder.ceval has a reduced total time of 0.653s.

MEX コード カバレッジに対する式の畳み込みの効果

coder.const を使用して式を定数に畳み込むとき、MATLAB 関数と MEX 関数の間のコード カバレッジに差異が生じます。たとえば、以下の関数について考えます。

function y = MyFoldFunction %#codegen
a = 1;
b = 2; 
c = a + b;
y = 5 + coder.const(c);
end

MATLAB 関数 MyFoldFunction をプロファイリングすると、プロファイル詳細レポートにこのコード カバレッジが表示されます。

Profile Detail Report, showing code coverage

しかし、MEX 関数 MyFoldFunction_mex をプロファイリングすると異なるコード カバレッジが表示されます。

Profile Detail Report, showing code coverage

コード生成では式 c = a + b を定数に畳み込んでいるため、行 2、3 および 4 は生成コードでは実行されません。

この例では、ユーザー定義の式の畳み込みを使用します。コード ジェネレーターは、生成コードのパフォーマンスを最適化するために、特定の式を自動的に畳み込む場合があります。こうした最適化も、MEX 関数のカバレッジが MATLAB 関数と異なるものになる要因です。

参考

| | | | |

トピック