メインコンテンツ

生成コードのパフォーマンスと可読性を微調整するためのインライン化の制御

インライン化は、関数呼び出しをその関数の本体と置き換える最適化手法です。インライン化によって関数呼び出しのオーバーヘッドが排除されるため、速度が向上します。インライン化により、生成された C/C++ コードがさらに最適化されることにもなります。

しかし、アプリケーションによっては、コードのインライン化が多すぎると次のような欠点が生じる可能性もあります。

  • インライン化によって、生成される C/C++ コードのサイズが大きくなり、コードの可読性が低下する可能性があります。たとえば、ソース MATLAB® コードで特定の関数 foo を何度も呼び出すとします。コード ジェネレーターが foo を常にインライン化する場合、foo は呼び出されるたびにインライン化されるため、生成コードのサイズは増加します。

  • 関数をインライン化すると、関数本体で定義された変数は呼び出し元関数のスコープの一部になります。したがって、これらの変数に割り当てられた領域は、インライン化された関数の実行が完了してもスタックから解放されません。関数がインライン化されていない場合、割り当てられたスタック領域は関数が戻るときに解放されます。

既定では、コード ジェネレーターは MEX 関数およびスタンドアロン コードを生成するときに速度を優先します。ただし、スタンドアロン コードに MathWorks® 関数を呼び出すユーザー記述関数、またはユーザー記述関数を呼び出す MathWorks 関数が含まれている場合、コード ジェネレーターはコードの可読性を優先します。このような場合、非常に小さな関数を除いて、コード ジェネレーターは通常、MathWorks コードとユーザー記述コードの間の呼び出しをインライン化しません。この動作により、ユーザー記述コードと MathWorks コード間の分離が維持されるため、生成されたコードがクリーンになります。この既定の設定を変更してインライン化設定を微調整し、アプリケーションの速度、可読性、およびスタック領域の要件を満たすコードを生成できます。

インライン化命令が競合する場合、コード ジェネレーターはインライン化制御階層に従い、生成されたコードで関数をインライン化するかどうかを決定します。

  • coder.inlineCall または coder.nonInlineCall を使用して関数を呼び出すと、他のすべてのインライン化制御がオーバーライドされます。

  • MATLAB 関数の本体内の coder.inline("always") または coder.inline("never") 命令により、グローバル インライン化設定およびコード構成設定はオーバーライドされます。

  • グローバル インライン化設定により、コード構成設定がオーバーライドされます。

  • 他のインライン化制御が競合しない場合、コード ジェネレーターは、コード構成設定によって指示されたインライン化の動作を示します。

呼び出しサイトでの特定の MATLAB 関数のインライン化の制御

呼び出しサイトで特定の MATLAB 関数のインライン化を制御するには、coder.inlineCall または coder.nonInlineCall を使用して関数を呼び出します。これらの関数により、呼び出される関数の本体内の coder.inline 命令、グローバル インライン化設定、コード構成設定などの他のインライン化制御がすべてオーバーライドされます。

関数内の特定の MATLAB 関数のインライン化の制御

特定の MATLAB 関数を常にインライン化する、または決してインライン化しないようにコード ジェネレーターに指示するには、その関数の本体内で coder.inline("always") 命令または coder.inline("never") 命令を使用します。これらの命令の詳細については、coder.inline を参照してください。

MATLAB 関数の本体内の coder.inline("always") または coder.inline("never") 命令により、グローバル インライン化設定およびコード構成設定はオーバーライドされます。一部の MathWorks 関数には、coder.inline 命令の呼び出しが含まれています。したがって、この命令により、グローバル インライン化設定およびコード構成設定がオーバーライドされます。このような関数のインライン化を強制または防止するには、coder.inlineCall または coder.nonInlineCall を使用します。

インライン化のグローバルな制御

コマンド ラインで codegen コマンドを使用してコードを生成する場合、オプション -O enable:inline を使用して、すべての関数をインライン化するようにコード ジェネレーターに指示できます。あるいは、オプション -O disable:inline を使用して、どの関数もインライン化しないようにコード ジェネレーターに指示できます。個別の関数について、関数の本体で coder.inline 命令を使用するか、coder.inlineCall または coder.nonInlineCall を使用して関数を呼び出すことによって、このオプションをオーバーライドできます。

-O disable:inline または -O enable:inline オプションを指定して codegen を使用してコードを生成した場合、そのオプションにより、コード構成設定 InlineBetweenUserFunctionsInlineBetweenMathWorksFunctions、および InlineBetweenUserAndMathWorksFunctions がオーバーライドされます。

コード構成設定を使用したインライン化の制御

ユーザー記述関数用に生成されたコードおよび MathWorks 関数用に生成されたコードの速度および可読性の要件が、コード生成の既定の設定とは異なる場合があります。コード構成設定を使用することで、MathWorks 関数が他の MathWorks 関数を呼び出すとき、ユーザー記述関数が他のユーザー記述関数を呼び出すとき、およびユーザー記述関数と MathWorks 関数の間で呼び出しが行われるときのインライン化動作を制御できます。MEX 関数生成に使用する設定は、スタンドアロン コード生成に使用する設定と異なる場合があります。

次の表に、さまざまなインライン化の目標に対応する設定を示します。

インライン化の目標コード構成オブジェクトのプロパティ

MATLAB Coder™ アプリのパラメーター

ユーザー記述関数が別のユーザー記述関数を呼び出しているすべての呼び出しサイトにおけるインライン化動作を制御する

InlineBetweenUserFunctions

ユーザー定義の MATLAB 関数に対するインライン手法

MathWorks 関数が別の MathWorks 関数を呼び出しているすべての呼び出しサイトにおけるインライン化の動作を制御する InlineBetweenMathWorksFunctions

MathWorks 定義の MATLAB 関数に対するインライン手法

ユーザー記述関数が MathWorks 関数を呼び出している、または MathWorks 関数がユーザー記述関数を呼び出しているすべての呼び出しサイトにおけるインライン化動作を制御する

InlineBetweenUserAndMathWorksFunctions

MathWorks 関数とユーザー関数の間の呼び出しに対するインライン手法

アプリケーションのニーズに応じて、次のパラメーターを設定します。

  • "Always" – すべての関数をインライン化します。

  • "Speed" – 内部のヒューリスティックな方法を使用して、関数をインライン化するかどうかを決定します。この設定では、最適化されたコードが優先されます。

  • "Readability" – インライン化の対象を非常に小さな関数のみにすることで、可読性を優先します。この設定により、コードのモジュール性と速度のバランスが取られ、パフォーマンスに悪影響を与えることなく、可読性の高いコードが生成されます。

  • "Never" – 関数をインライン化しません。この設定は、生成されたコードのパフォーマンスに悪影響を及ぼす可能性があります。

コード構成設定は、グローバル インライン化制御または特定の関数に適用されたインライン化制御によってオーバーライドされます。

インライン化戦略の例

生成されたコードの速度と可読性のバランスを取るために、次のアクションを実行するようにコード ジェネレーターに指示できます。

  • 生成されたコードの速度が低下するとしても、可読性を高めるためにユーザー記述コードのモジュール性を維持する。この動作のためには、InlineBetweenUserFunctions"Readability" に設定します。

  • コードの可読性が低下するとしても、コード ベースの該当部分を検査する可能性が低いため、MathWorks 関数の高度に最適化されたコードを生成する。この動作のためには、InlineBetweenMathWorksFunctions"Speed" に設定します。

  • ユーザー記述コードと MathWorks コード間のモジュール性を維持する。この動作のためには、InlineBetweenUserAndMathWorksFunctions"Readability" に設定します。インライン化が "Readability" に設定されている場合、インライン化されるのは非常に小さな MathWorks 関数に限られるため、生成されたコードは MATLAB コードに類似したものになります。

例: ユーザー記述関数と MathWorks 関数間のインライン化の制御

この例では、ユーザー記述関数が MathWorks 関数を呼び出しているすべての呼び出しサイトにおけるインライン化動作を制御する方法を示します。

MathWorks 関数を呼び出す関数の定義

double の配列 x を入力として受け入れ、bessely関数を使用して入力配列を処理し、x と同じ型およびサイズをもつ配列を返す useBessely という名前の MATLAB 関数を定義します。

type useBessely.m
function out = useBessely(x)
out = x + bessely(3,x);
end

既定のインライン化設定を使用したスタンドアロン コードの生成

cfg という名前のコード構成オブジェクトを定義して、C++ スタティック ライブラリのソース コードを生成します。既定では、コード ジェネレーターは、スタンドアロン コードでユーザー記述関数と MathWorks 関数間の呼び出しが行われているときに可読性を最適化します。コードの可読性を維持するために、コード ジェネレーターは通常、ユーザー記述関数によって呼び出される MathWorks 関数をインライン化しません。

cfg = coder.config("lib");
cfg.TargetLang = "C++";
cfg.GenCodeOnly = true;

cfg オブジェクトを使用して useBessely 関数のコードを生成し、1 行 100 列の double 型として入力を指定します。-report オプションを使用してレポートを生成し、-d オプションを使用して生成されたコード用のフォルダー readable_version を作成します。

codegen -config cfg useBessely -args {zeros(1,100)} -report -d readable_version
Code generation successful: View report

ファイル useBessely.cpp 内の生成された C++ コードを検査します。C++ 関数 useBessely が MathWorks 関数 bessely に対して生成されたコードを含む別の C++ 関数 coder::bessely を呼び出していることを確認します。この動作により、記述した MATLAB 関数から生成されたコードは、MathWorks 関数から生成されたコードとは別個に維持されます。

type(fullfile("readable_version","useBessely.cpp"))
//
// File: useBessely.cpp
//
// MATLAB Coder version            : 25.1
// C/C++ source code generated on  : 13-Jul-2025 16:34:40
//

// Include Files
#include "useBessely.h"
#include "bessely.h"
#include "rt_nonfinite.h"

// Function Definitions
//
// Arguments    : const double x[100]
//                creal_T out[100]
// Return Type  : void
//
void useBessely(const double x[100], creal_T out[100])
{
  coder::bessely(x, out);
  for (int i{0}; i < 100; i++) {
    out[i].re += x[i];
  }
}

//
// File trailer for useBessely.cpp
//
// [EOF]
//

変更されたインライン化設定を使用したコードの生成

ユーザー記述関数によって呼び出される MathWorks 関数をインライン化するかどうかを決定するために内部のヒューリスティックな方法を使用するようにコード ジェネレーターに指示するには、コード構成オブジェクトのプロパティ InlineBetweenUserAndMathWorksFunctions'Speed' に設定します。この設定により、高度にモジュール化されたコードよりも効率的ではあるが、可読性が劣る C++ コードが生成される可能性があります。

cfg.InlineBetweenUserAndMathWorksFunctions = 'Speed';

cfg コード構成オブジェクトを使用してコードを生成します。1 行 100 列の double 型として入力を指定します。-report オプションを使用してレポートを作成し、-d オプションを使用して生成されたコード用のフォルダー speed_version を作成します。

codegen -config cfg useBessely -args {zeros(1,100)} -report -d speed_version 
Code generation successful: View report

ファイル useBessely.cpp 内の生成された C++ コードを検査します。MathWorks 関数 bessely に対して個別の C++ 関数が生成されていないことを確認します。代わりに、コード ジェネレーターにより、MathWorks の bessely 関数のコードが、記述した useBessely 関数にインライン化されています。

type(fullfile("speed_version","useBessely.cpp"))
//
// File: useBessely.cpp
//
// MATLAB Coder version            : 25.1
// C/C++ source code generated on  : 13-Jul-2025 16:34:59
//

// Include Files
#include "useBessely.h"
#include "cbinu.h"
#include "cbknu.h"
#include "cospiAndSinpi.h"
#include "cuoik.h"
#include "rt_nonfinite.h"
#include <cmath>

// Function Definitions
//
// Arguments    : const double x[100]
//                creal_T out[100]
// Return Type  : void
//
void useBessely(const double x[100], creal_T out[100])
{
  creal_T CY;
  creal_T b_CY;
  creal_T cy;
  double AZ;
  for (int k{0}; k < 100; k++) {
    double d;
    int ierr;
    d = x[k];
    ierr = 0;
    if (std::isnan(d)) {
      CY.re = rtNaN;
      CY.im = 0.0;
    } else {
      if (d == 0.0) {
        CY.re = rtMinusInf;
        CY.im = 0.0;
      } else if (d == 0.0) {
        CY.re = 0.0;
        CY.im = 0.0;
        ierr = 1;
      } else {
        creal_T ZN;
        double b_AZ;
        double re;
        int NUF;
        CY.re = 0.0;
        CY.im = 0.0;
        if (d == 0.0) {
          ierr = 1;
        } else {
          ierr = 0;
          ZN.re = d * 0.0;
          ZN.im = -d;
          AZ = std::abs(d);
          if (!(AZ > 0.0)) {
            AZ = 0.0;
          }
          if (AZ > 1.0737418235E+9) {
            ierr = 4;
          } else if (AZ > 32767.999992370605) {
            ierr = 3;
          }
          if (AZ < 2.2250738585072014E-305) {
            ierr = 2;
          } else {
            NUF = coder::cuoik(ZN, 2, CY);
            if (NUF < 0) {
              ierr = 2;
            } else if (NUF <= 0) {
              coder::cbknu(ZN, CY);
              ZN = CY;
              AZ = 1.0;
              if (std::fmax(std::abs(CY.re), std::abs(CY.im)) <=
                  1.0020841800044864E-289) {
                ZN.re = 4.503599627370496E+15 * CY.re;
                ZN.im = 4.503599627370496E+15 * CY.im;
                AZ = 2.2204460492503131E-16;
              }
              b_AZ = ZN.re * 0.63661977236758138 - ZN.im * 0.0;
              re = ZN.re * 0.0 + ZN.im * 0.63661977236758138;
              CY.re = AZ * b_AZ;
              CY.im = AZ * re;
            }
          }
        }
        if ((ierr == 0) || (ierr == 3)) {
          b_CY.re = 0.0;
          b_CY.im = 0.0;
          if (d == 0.0) {
            ierr = 1;
          } else {
            ierr = 0;
            AZ = d * 0.0;
            ZN.re = AZ;
            ZN.im = d;
            b_AZ = std::abs(d);
            if (!(b_AZ > 0.0)) {
              b_AZ = 0.0;
            }
            if (b_AZ > 1.0737418235E+9) {
              ierr = 4;
            } else if (b_AZ > 32767.999992370605) {
              ierr = 3;
            }
            if (b_AZ < 2.2250738585072014E-305) {
              ierr = 2;
            } else {
              NUF = coder::cuoik(ZN, 2, b_CY);
              if (NUF < 0) {
                ierr = 2;
              } else if (NUF <= 0) {
                boolean_T guard1;
                boolean_T guard2;
                guard1 = false;
                guard2 = false;
                if ((AZ == 0.0) && (d < 0.0)) {
                  ZN.re = -AZ;
                  ZN.im = -d;
                  NUF = coder::cbinu(ZN, b_CY);
                  if (NUF < 0) {
                    ierr = -1;
                    if (NUF == -2) {
                      ierr = -2;
                    }
                    guard2 = true;
                  } else {
                    cy.re = 0.0;
                    cy.im = 0.0;
                    NUF = coder::cbknu(ZN, cy);
                    if (NUF != 0) {
                      ierr = -1;
                      if (NUF == -2) {
                        ierr = -2;
                      }
                      guard2 = true;
                    } else {
                      double im;
                      b_AZ = coder::internal::scalar::cospiAndSinpi(-0.0, AZ);
                      re = 0.0 * b_CY.re - -3.1415926535897931 * b_CY.im;
                      im = 0.0 * b_CY.im + -3.1415926535897931 * b_CY.re;
                      b_CY.re = re + (-b_AZ * cy.re - -AZ * cy.im);
                      b_CY.im = im + (-b_AZ * cy.im + -AZ * cy.re);
                      guard1 = true;
                    }
                  }
                } else {
                  coder::cbknu(ZN, b_CY);
                  guard1 = true;
                }
                if (guard2) {
                  if (ierr == -1) {
                    ierr = 2;
                  } else {
                    ierr = 5;
                  }
                }
                if (guard1) {
                  ZN = b_CY;
                  AZ = 1.0;
                  if (std::fmax(std::abs(b_CY.re), std::abs(b_CY.im)) <=
                      1.0020841800044864E-289) {
                    ZN.re = 4.503599627370496E+15 * b_CY.re;
                    ZN.im = 4.503599627370496E+15 * b_CY.im;
                    AZ = 2.2204460492503131E-16;
                  }
                  b_AZ = ZN.re * 0.63661977236758138 - ZN.im * -0.0;
                  re = ZN.re * -0.0 + ZN.im * 0.63661977236758138;
                  b_CY.re = AZ * b_AZ;
                  b_CY.im = AZ * re;
                }
              }
            }
          }
          if ((ierr == 0) || (ierr == 3)) {
            CY.re = b_CY.re - CY.re;
            CY.im = b_CY.im - CY.im;
            AZ = 0.0 * CY.re - 0.5 * CY.im;
            b_AZ = 0.0 * CY.im + 0.5 * CY.re;
            CY.re = AZ;
            CY.im = b_AZ;
          }
        }
      }
      if (ierr == 5) {
        CY.re = rtNaN;
        CY.im = 0.0;
      } else if (ierr == 2) {
        CY.re = rtInf;
        CY.im = 0.0;
      }
      if (d > 0.0) {
        AZ = CY.re;
        CY.re = AZ;
        CY.im = 0.0;
      }
    }
    out[k].re = d + CY.re;
    out[k].im = CY.im;
  }
}

//
// File trailer for useBessely.cpp
//
// [EOF]
//

参考

| | | | | |

トピック