mcpField
M
matlab-upgrade-mex-ic

matlab-upgrade-mex-ic

github.com/matlab開発1.2k52 回インストール1 日前に更新
リポジトリ

matlab-upgrade-mex-ic について

C、C++、Fortran の MEX ソースを Separate Complex API から Interleaved Complex API に移行する MATLAB スキルです。ソース変更、SC/IC 互換性の選択、ビルド、結果検証を支援します。

主な機能

  1. 01旧 Separate Complex API 呼び出しの特定
  2. 02旧アクセサーから型付き IC API への対応付け
  3. 03IC 専用または SC/IC 条件付きソースへの対応
  4. 04明示的な MATLAB API モードでのビルド
  5. 05変換後の MEX 出力の検証

活用シーン

  1. 01`mex -R2018a` 向けの旧 MEX ソースの更新
  2. 02SC と IC の両ビルドに対応するソースの維持
  3. 03変換後の複素配列の結果確認
  4. 04旧 C、C++、Fortran MEX コードの点検

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-upgrade-mex-ic

インストール後、Claude Code で/matlab-upgrade-mex-icを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

manifest.yaml
references/
api-mappings.md
pitfalls.md

詳細

MEX ファイルをインターリーブ複素数 API にアップグレードする

MathWorks のアップグレード手順に従い、SC/IC ガード付きビルドで MX_HAS_INTERLEAVED_COMPLEX を使用する場合の注意点、必須変更と推奨変更、および検証を考慮して、C、C++、Fortran の MEX ソースファイルを Separate Complex(SC)API から Interleaved Complex(IC)API に変換します。

使用する場合

  • mxGetPr/mxGetPi、mxGetData/mxGetImagData、または mxSetPr/mxSetPi を使用する C、C++、または Fortran の MEX ファイルを扱う場合
  • mex -R2018a を使用してビルドしたい場合
  • インターリーブ複素数、IC MEX、または MEX コードのモダナイズについて質問された場合
  • File Exchange、GitHub、または R2018a より前の時代のレガシー MEX コードを扱う場合
  • C Matrix API(mxGetPr/mxGetPi など)を使用する C++ MEX ファイル(.cpp、.cxx)を扱う場合
  • %val() ポインターを介して mxGetPr/mxGetPi を使用する Fortran MEX ファイル(.F、.f90)を扱う場合
  • 大きな複素数配列で MEX 関数が遅いと報告された場合(ループではなく呼び出しのオーバーヘッド)
  • 複素数データに対する MEX のパフォーマンスを改善する方法を質問された場合

使用しない場合

  • 新しい MEX 関数を一から作成する場合 — 変換は不要ですが、複素数データのパフォーマンス向上と、レガシーの mxGetPr/mxGetPi に代わる、最新の型安全なデータアクセスのため、最初から IC API(mxGetDoubles、mxGetComplexDoubles など)を使用するよう案内してください
  • C++ MEX API(matlab::mex::Function)を使用する場合 — API がまったく異なります。このスキルの対象は、C Matrix API(mex.h)を使用する C++ ファイルであり、MATLAB Data API for C++ や C++ Engine ではありません
  • MEX ファイルが型付きアクセサー(mxGetDoubles、mxGetComplexDoubles など)をすでに使用している場合 — すでに IC 互換であり、変換は不要です

ワークフロー

以下の 7 つの手順を順番に実行します。手順を省略しないでください。

手順 1:アップグレード前の確認

変更を加える前に、現在の状態を把握します。

  1. ソースファイル全体を読みます
  2. SC API の呼び出しをすべて特定します(主な API の対応関係を参照)
  3. MEX が扱うデータ型(double、single、int32 など)を記録します
  4. 複素数データを扱うかどうかを記録します(mxGetPi、mxIsComplex、mxCOMPLEX)

ユーザーに次の内容を報告します。

  • 見つかった SC API 呼び出しの数
  • 複素数を処理するファイルかどうか(SC モードでは、複素数配列にデインターリーブ/再インターリーブのオーバーヘッドが発生します)
  • パフォーマンスへの影響: ファイルが複素数データを扱う場合、SC MEX 呼び出しでは MATLAB 境界で O(n) のデインターリーブ/再インターリーブコピーが発生することを伝えます。大きな配列では、IC への変換によってこのオーバーヘッドを完全に解消できます(IC 変換によるパフォーマンス上のメリットを参照)。
  • 気付いたその他の問題(エラーチェックの不足など)
手順 2:出力ファイルの決定

作業を進める前に、ユーザーに確認します。

「変換したコードはどこに書き出しますか?

  • 新しいファイル — 元のファイルは変更せずに、別のファイル(例:<name>_ic.c または <name>_dual.c)を作成します
  • 元のファイルを直接変更 — 元のファイルを直接編集します。ファイルがバージョン管理下にあり、必要に応じて元に戻せる場合に選んでください。」

ユーザーが新しいファイルを選択した場合は、次の名前を既定値として使用します(またはユーザー指定の名前を使用します)。

  • IC のみ:<name>_ic.c/<name>_ic.F
  • SC/IC ガード付き:<name>_dual.c/<name>_dual.F

ユーザーが元のファイルを直接変更を選択した場合は、ファイルを復元できることを確認します(例:「このファイルは git で追跡されています。必要に応じて git checkout <file> で元に戻せます。」)。その後、元のファイルを直接編集します。

書き込み先のファイルと、その理由をユーザーに伝えます。

ステップ 3:判断ポイント — IC のみ、または SC/IC ガード付き?

「SC/IC ガード付き」とは何ですか? #if MX_HAS_INTERLEAVED_COMPLEX プリプロセッサーガードを使用して、分離複素数 API(mex -R2017b)とインターリーブ複素数 API(mex -R2018a)の両方でコンパイルできる、単一のソースファイルです。コンパイラーは IC モードでは MX_HAS_INTERLEAVED_COMPLEX=1、SC モードでは 0 を定義するため、ガードによってビルド時に正しいコードパスが選択されます。

続行する前に、ユーザーに確認してください:

「この MEX ファイルを、旧 SC API と新 IC API の両方でコンパイルできるようにする必要がありますか?

  • SC/IC ガード付き — コードを #if MX_HAS_INTERLEAVED_COMPLEX / #else ガードで囲み、同じソースから mex -R2017b file.c(SC モード)と mex -R2018a file.c(IC モード)の両方でビルドできるようにします。複数の MATLAB バージョンをサポートする場合は、こちらを選択してください。
  • IC のみ — IC API に完全に変換します。ビルドには mex -R2018a が必要です。コードはシンプルになりますが、R2018a 以降でのみ動作します。」

ユーザーが SC/IC ガード付き を選択した場合は、#if MX_HAS_INTERLEAVED_COMPLEX プリプロセッサーパターンを使用してください:

C/C++ ファイル:

#if MX_HAS_INTERLEAVED_COMPLEX
    /* IC code path */
    mxComplexDouble *pc = mxGetComplexDoubles(prhs[0]);
#else
    /* SC code path */
    double *pr = mxGetPr(prhs[0]);
    double *pi = mxGetPi(prhs[0]);
#endif

Fortran ファイル(.F プリプロセス済みソース):

#if MX_HAS_INTERLEAVED_COMPLEX
      mwPointer mxGetComplexDoubles
      complex*16, pointer :: pc(:)
      call mxGetComplexDoubles(prhs(1), pc)
#else
      mwPointer mxGetPr, mxGetPi
      mwPointer pr, pi
      pr = mxGetPr(prhs(1))
      pi = mxGetPi(prhs(1))
#endif

Fortran に関する注意: SC/IC ガード付きの Fortran ファイルでは、MEX コンパイラーが C プリプロセッサーを呼び出すように、拡張子に .F(大文字)を必ず使用してください。拡張子が .f または .f90 の場合はプリプロセスがスキップされ、#if ガードが機能しません。プリプロセッサーガードを使用できない .f90 ファイルでは、IC のみの変換を使用してください。

ユーザーが IC のみ を選択した場合は、ガードを使わずに直接変換してください。

ステップ 4:段階的なリファクタリング

IC への完全なアップグレードでは、古いデータアクセス API をすべて変換する必要があります。この表にあるパターンをすべて変換してください:

古いパターン置き換え先理由
データアクセス用の mxGetPi(arr)mxGetComplexDoubles(arr) + .imagIC には存在しません
mxSetPi(arr, ptr)mxSetComplexDoubles(arr, ptr)IC には存在しません
mxGetImagData(arr)型固有の複素数アクセサーIC には存在しません
mxSetImagData(arr, ptr)型固有の複素数セッターIC には存在しません
複素数かどうかの確認に使う mxGetPi(arr) != NULLmxIsComplex(arr) — SC と IC の両方で動作しますIC には存在しません
mxGetPr(arr)mxGetDoubles(arr)IC では複素数配列に対して誤った結果になります。型指定もありません
mxSetPr(arr, ptr)mxSetDoubles(arr, ptr)IC では複素数配列に対して誤った結果になります。型指定もありません
mxGetData(arr) + void* キャスト型固有のアクセサー(mxGetDoubles、mxGetInt32s など)IC では複素数配列に対して誤った結果になります。型指定もありません
mxSetData(arr, ptr)型固有のセッター(mxSetDoubles、mxSetInt32s など)IC では複素数配列に対して誤った結果になります。型指定もありません

なぜすべてが必須なのですか? IC モードで複素数配列に対して mxGetPr/mxGetData を使うと、インターリーブされたデータ(実数部と虚数部が交互に並ぶ)へのポインターが返されます。実数のみのデータとして反復処理すると、気付かないうちに誤った結果になります。実数のみの配列では引き続き動作しますが、型指定がありません(void* または常に double* です)。IC にアップグレードする際は、正確性と型安全性のため、すべてを一度に変換してください。

よくある変換ミス(複素数かどうかの確認、出力の割り当て、バッファーサイズ、Fortran のインターリーブレイアウト)については、references/pitfalls.md を参照してください。

SC/IC ガード付きモードでのコード重複の削減

SC/IC ガード付きモードでは、ロジック全体ではなくアクセサー呼び出しだけをガードして、コードの重複を最小限にしてください。

C/C++ ファイル:

/* Guard only the pointer acquisition — logic stays shared */
double *pr_in, *pr_out;

#if MX_HAS_INTERLEAVED_COMPLEX
    pr_in = mxGetDoubles(prhs[0]);
#else
    pr_in = mxGetPr(prhs[0]);
#endif

plhs[0] = mxCreateDoubleMatrix(m, n, mxREAL);

#if MX_HAS_INTERLEAVED_COMPLEX
    pr_out = mxGetDoubles(plhs[0]);
#else
    pr_out = mxGetPr(plhs[0]);
#endif

/* Shared logic — no duplication */
for (i = 0; i < numElements; i++) {
    pr_out[i] = pr_in[i] * factor;
}

Fortran ファイル(.F):

c     Guard only the pointer acquisition
#if MX_HAS_INTERLEAVED_COMPLEX
      pr_in = mxGetDoubles(prhs(1))
#else
      pr_in = mxGetPr(prhs(1))
#endif

c     Shared logic — no duplication
      call mxCopyPtrToReal8(pr_in, data, numElements)
      do i = 1, numElements
          data(i) = data(i) * factor
      end do

複素数データでは、モードによってレイアウトが異なるため(インターリーブ構造体と分離配列)、ロジック全体を囲む #if/#else ブロックは避けられません。

経験則:

  • 実数データのパス — アクセサーだけをガードし、ループは共通化します
  • 複素数データのパス — ブロック全体をガードします(データレイアウトが異なるため、ループ本体も別にする必要があります)。C、C++、Fortran での複素数ループの例については、references/api-mappings.md の「SC/IC Guarded Pattern」を参照してください。

関数の完全な対応表については、references/api-mappings.md を参照してください。

ステップ 5:ビルド

正確なビルドコマンドを指定してください(言語に合わせて拡張子を調整してください)。

% C/C++ files
mex -R2018a <filename>.c       % Explicit IC mode
mex -R2017b <filename>.c       % Explicit SC mode
mex <filename>.c               % Default (version-dependent)
% For C++: same flags with .cpp or .cxx extension
mex -R2018a <filename>.cpp     % Explicit IC mode

% Fortran files (.F with preprocessor guards)
mex -R2018a <filename>.F       % Explicit IC mode
mex -R2017b <filename>.F       % Explicit SC mode
mex <filename>.F               % Default (version-dependent)

ファイルが SC/IC ガード付きの場合は、-R2017b(SC)、-R2018a(IC)、デフォルト(フラグなし)の 3 つすべてのビルドモードをテストするようユーザーに伝えてください。

Fortran: プリプロセッサガードを機能させるには、ファイルの拡張子を大文字の .F にする必要があります。ソースが .f または .f90 の場合は、#if ガードを追加する際に .F または .F90 に名前を変更してください。

ステップ 6:検証

変換後の MEX と元の MEX を比較する MATLAB テストコマンドを指定してください。

IC 専用の変換(ソースファイルを分ける場合):

%% Build original with SC, converted with IC
mex -R2017b originalFile.c -output func_sc    % Explicit SC build
mex -R2018a convertedFile_ic.c -output func_ic % Explicit IC build

%% Compare outputs
Z = complex(rand(4,4), rand(4,4));
assert(isequal(func_sc(Z), func_ic(Z)), 'Output mismatch!');

SC/IC ガード付きファイル(同じソース、3 つのビルドモード):

%% Build same source in all three modes
mex -R2017b convertedFile.c -output func_sc       % Explicit SC (forces separate complex)
mex -R2018a convertedFile.c -output func_ic       % Explicit IC (forces interleaved complex)
mex convertedFile.c -output func_default           % Default (no API flag)

%% Compare outputs — all three must match
Z = complex(rand(4,4), rand(4,4));
out_sc = func_sc(Z);
out_ic = func_ic(Z);
out_default = func_default(Z);
assert(isequal(out_sc, out_ic), 'SC vs IC mismatch!');
assert(isequal(out_sc, out_default), 'SC vs default mismatch!');

必ず次のテストを行ってください:

  • 複素数入力(MEX が複素データを扱う場合)
  • 実数入力(MEX が実数データを扱う場合)
  • エッジケース:空の配列、スカラー、大きな配列
  • 3 つすべてのビルドモード:-R2017b(SC)、-R2018a(IC)、デフォルト(フラグなし)
  • 元のソースファイルを mex -R2017b で引き続きコンパイルできること(変更されていないことを確認します)
ステップ 7:ドキュメント化

変換後のファイルのヘッダーコメントにビルド情報を追加してください。

/*
 * Compile (Interleaved Complex API, R2018a+):
 *   mex -R2018a filename.c
 *
 * Original (Separate Complex API):
 *   mex originalFilename.c
 *
 * API Migration:
 *   mxGetPr/mxGetPi -> mxGetComplexDoubles
 *   mxSetPr/mxSetPi -> mxSetComplexDoubles
 */

IC 変換によるパフォーマンス上の利点

IC 変換は単なる API の最新化ではなく、複素数データを扱う MEX 関数に測定可能なパフォーマンス向上をもたらします。

IC の方が高速な理由

R2018a 以降、MATLAB は複素数配列を内部的にインターリーブ形式([re0, im0, re1, im1, ...])で格納します。SC API でコンパイルされた MEX 関数が呼び出されると:

  1. 呼び出し時: MATLAB は入力をデインターリーブし、連続した 1 つのバッファーを 2 つの別々の実数/虚数配列に分割します。これは O(n) のコピーです。
  2. 終了時: MATLAB は出力を再インターリーブし、別々のバッファーを 1 つにまとめます。これも O(n) のコピーです。

1,000 万要素の複素数倍精度配列では、境界を越える複素数配列 1 つにつき約 80 MB になります(N × 16 バイト)。複素数入力 1 つと複素数出力 1 つを持つ MEX では合計約 160 MB、複素数入力を 3 つ持つ MEX では入力だけで約 240 MB のコピーが発生します。

IC MEX 関数は MATLAB のネイティブレイアウトでデータにアクセスするため、境界でのコピーはゼロです。

その他のパフォーマンス上の利点
  • キャッシュ局所性: z[i].real と z[i].imag は隣接しています(同じキャッシュライン)。SC では、数百万要素離れた 2 つの配列間を移動する必要があります。
  • 一括コピーでの memcpy は 1 回: 複素数配列の複製は、別々に 2 回コピーする代わりに、memcpy(dst, src, n * sizeof(mxComplexDouble)) で 1 回で済みます。
  • FFTW/BLAS/LAPACK との相互運用: IC レイアウトは fftw_complex、double _Complex(C99)、cuBLAS/cuFFT 型とバイト単位で互換性があるため、パック/アンパックは不要です。
  • SIMD に適している: コンパイラーは、1 回の操作で複素数全体を読み込むパック倍精度命令を生成できます。
パフォーマンスを理由に IC へ移行すべきケース
シナリオ高速化の効果
大きな複素数配列(>100K 要素)高 — 境界でのコピーが支配的です
ループ内で MEX を頻繁に呼び出す高 — コピーのオーバーヘッドが蓄積します
MEX が FFTW/BLAS の呼び出しをラップする高 — パック/アンパックが不要になります
小さな配列(<1K 要素)低 — コピーのオーバーヘッドは無視できます
実数のみのデータ(mxGetPi なし)なし — デインターリーブは発生しません

ユーザーに次のように伝えてください:「[N] 要素の複素数配列の場合、SC API では MATLAB が複素数配列の引数 1 つにつき約 [N×16/1e6] MB をコピーします。境界を越える複素数配列が [K] 個あると、呼び出し 1 回あたり約 [K×N×16/1e6] MB になります。IC ではこれを完全になくせます。」

ベンチマークのテンプレート

変換後、速度向上を定量化するため、次のベンチマークを提示してください。

%% Benchmark SC vs IC MEX performance
N = 1e7;  % 10M complex elements
Z = randn(1,N) + 1i*randn(1,N);
numIter = 20;

% Build both versions
mex -R2017b originalFile.c -output func_sc
mex -R2018a convertedFile_ic.c -output func_ic

% Warmup
for k = 1:3, func_sc(Z); func_ic(Z); end

% Time SC
tSC = zeros(numIter,1);
for k = 1:numIter, tic; func_sc(Z); tSC(k) = toc; end

% Time IC
tIC = zeros(numIter,1);
for k = 1:numIter, tic; func_ic(Z); tIC(k) = toc; end

fprintf('SC median: %.2f ms\n', median(tSC)*1000);
fprintf('IC median: %.2f ms\n', median(tIC)*1000);
fprintf('Speedup: %.1fx\n', median(tSC)/median(tIC));
assert(isequal(func_sc(Z), func_ic(Z)), 'Output mismatch!');
最適化のヒント
  • インプレース風の操作には mxDuplicateArray を使用する: plhs[0] = mxDuplicateArray(prhs[0]); z = mxGetComplexDoubles(plhs[0]); — その後、z[i] を直接変更します。手動での割り当てを避け、MATLAB によるコピーの最適化を可能にします。
  • 単一パスのループ: IC モードでは、共役複素数化やスケーリングの際に、空間局所性を最大限に活かしながら、1 回のループ反復で .real と .imag の両方を処理できます。
  • 不要な mxMalloc を避ける: 出力を mxCOMPLEX として作成し、mxGetComplexDoubles から取得したバッファーに直接書き込みます。中間の割り当ては不要です。

主要な API の対応関係

最も一般的な変換の概要です。全表は references/api-mappings.md を参照してください。

複素データへのアクセス
SC APIIC API種別
mxGetPr + mxGetPimxGetComplexDoubles必須
mxSetPr + mxSetPimxSetComplexDoubles必須
mxGetImagData型固有の複素アクセサー必須
mxSetImagData型固有の複素セッター必須
mxGetPi(x) != NULLmxIsComplex(x)必須
実数データへのアクセス(IC では非推奨)
SC APIIC API状態
mxGetPrmxGetDoubles非推奨 — 置き換えてください
mxSetPrmxSetDoubles非推奨 — 置き換えてください
mxGetDatamxGetDoubles/mxGetInt32s/etc.非推奨 — 置き換えてください
mxSetDatamxSetDoubles/mxSetInt32s/etc.非推奨 — 置き換えてください
複素データ型(IC モード)
mxComplexDouble  /* struct with .real and .imag (both double) */
mxComplexSingle  /* struct with .real and .imag (both float) */
mxComplexInt32   /* struct with .real and .imag (both int32_T) */

規約

  • 書き込みを行う前に、必ず新しいファイルを作成するか、既存ファイルをそのまま変更するかを確認してください
  • 変更ごとに、その理由(IC には存在しない、または複素配列/型指定なしの場合に誤った結果になる)を必ず説明してください
  • 変換前に、SC/IC の条件付きモードを使うか、IC 専用にするかを必ず確認してください
  • 検証用のテストコマンドを必ず提示してください
  • 複素数かどうかの確認には mxIsComplex() を使用してください — mxGetPi() != NULL は使用しないでください。mxIsComplex は SC モードと IC モードの両方で動作するため、#if ガードの外に配置できます。
  • IC モードでのバッファーサイズ: mxGetElementSize() はインターリーブされた完全なサイズ(複素倍精度の場合は 16 バイト)を返します。2 倍しないでください。
  • ファイル名(新しいファイルを作成する場合): IC 専用の変換では _ic.c/_ic.cpp または _ic.F のサフィックス、SC/IC 条件付きの変換では _dual.c/_dual.cpp/_dual.F を使用します。ユーザーが選択した場合は、ファイルをそのまま変更してもかまいません。
  • C89 との互換性(C ファイルのみ): 各 #if/#else ブロックの先頭、または関数の先頭で変数を宣言してください。MEX コンパイラーでは、宣言を文より前に置く必要がある C89 の規則が適用される場合があります。C++ ファイルにはこの制限はありません。
  • Fortran のファイル拡張子: SC/IC 条件付きの Fortran ファイルには .F(大文字)を使用し、C プリプロセッサが #if MX_HAS_INTERLEAVED_COMPLEX ガードを処理するようにしてください。.f と .f90 ではプリプロセッサは実行されません。
  • Fortran のポインター処理: Fortran MEX では、データポインターは mwPointer 整数であり、コピー用ルーチン(mxCopyPtrToReal8、mxCopyPtrToComplex16)に %val() を介して渡します。IC モードでは、インターリーブされたバッファーへの mwPointer を返す mxGetComplexDoubles を使用してください。

よくある間違い

間違い問題となる理由正しい方法
確認せずに変更するバージョン管理されていない場合、ユーザーが動作する SC 版を失う可能性があります必ず「新しいファイルを作成しますか、それとも既存ファイルをそのまま変更しますか?」と確認してください
mxGetPr/mxGetData の呼び出しを変更せずに残すMathWorks は IC MEX 関数での使用を推奨していません型付きアクセサー(mxGetDoubles、mxGetInt32s など)に置き換えてください
複素バッファーに numElements * mxGetElementSize(arr) * 2 を使うmxGetElementSize は IC モードですでに 16 を返します2 倍せず、numElements * mxGetElementSize(arr) を使用してください
複素数かどうかの確認に mxGetPi(arr) != NULL を使うmxGetPi は IC モードには存在しませんmxIsComplex(arr) を使用してください
互換性の要件を確認せずに変換するユーザーは古い MATLAB でもファイルを動作させる必要があるかもしれません必ず「IC 専用ですか、それとも SC/IC 条件付きですか?」と確認してください
検証手順を提示しないMEX で誤った結果に気づかないままになると、深刻な問題につながります古い出力と新しい出力を比較するテストコマンドを必ず提示してください

Copyright 2026 The MathWorks, Inc.


コメント