mcpField
M
matlab-optimize-gpu-codegen

matlab-optimize-gpu-codegen

github.com/matlabその他1.2k39 回インストール2 日前に更新
リポジトリ

matlab-optimize-gpu-codegen について

GPU Coder向けに既存のMATLAB設計を反復最適化するスキルです。MEX、ライブラリ、DLLを対象に、コード生成、ベンチマーク、数値等価性の確認、GPU性能診断を行います。

主な機能

  1. 01GPU Coderのコンパイルワークフロー
  2. 02生成GPUコードのベンチマーク
  3. 03数値等価性の検証
  4. 04GPU性能診断の分析
  5. 05構造的なコード最適化

活用シーン

  1. 01既存MATLAB設計から生成するGPU MEXの高速化
  2. 02GPU Coderのコンパイルエラーの調査
  3. 03GPUメモリ使用量の削減や並列性の改善

インストール

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

$npx skills add https://github.com/matlab/matlab-agentic-toolkit --skill matlab-optimize-gpu-codegen

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

同梱ファイル

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

manifest.yaml
references/
gpu-codegen-functions.md
scripts/
benchmarkMex.m
extractDiagnostics.p

詳細

MATLABをGPUコード生成向けに最適化する

MATLABの設計ファイルをGPU Coder向けに反復的に最適化します。コンパイルとベンチマークを行い、構造的な最適化を適用し、gpuPerformanceAnalyzerでプロファイルし、診断結果を修正して、各段階で数値的な等価性を検証します。

使用する場合

  • ユーザーがMATLAB関数を持っており、より高速なGPU MEXまたはCUDAコードを求めている
  • ユーザーがGPU Coder、codegen、CUDA、gpuPerformanceAnalyzerに言及している
  • ユーザーが生成されたGPU/CUDAコードまたはGPU MEXのプロファイリングを求めている(生成されたGPUコードのプロファイリングが、このスキルの診断修正ワークフローの開始点です)
  • ユーザーがGPUメモリの削減、カーネルの並列性の向上、またはPerformance Analyzerの診断結果の修正を求めている
  • ユーザーが.m設計ファイルと代表的な入力を用意している

使用しない場合

  • codegenを使用しないワークフロー
  • coder.gpuConfig("exe") — スタンドアロン実行ファイルはMATLABからベンチマークも等価性チェックもできません。ユーザーにmexまたはlib/dllへ切り替え、最終的に最適化されたソースからexeを再生成するよう提案してください。
  • SimulinkのGPUコード生成
  • MATLAB関数を一から新規作成する場合(このスキルは既存コードを最適化します)
  • 設計ファイルから呼び出されるヘルパー関数を最適化する場合(このスキルはメインの設計ファイルのみを最適化します)
  • ハードウェアのセットアップまたはCUDA Toolkitのインストール
  • GPUを使わない一般的なMATLABのパフォーマンス調整
  • ユーザーのプロンプトにGPU、codegen、CUDA、MEX、またはプロファイリングへの言及がない場合。アクティベーションはユーザーのプロンプトのみに基づいて行ってください。ファイル名や関数の内容からGPUを使う意図を推測してはいけません。不確かな場合は、アクティベートする前に確認してください。

ワークフロー

セットアップ — セッションディレクトリの作成

すべてのcodegenアーティファクト、プロファイリング出力、最適化済みバージョンを、セッション全体で単一の一時ディレクトリに保存します。すべてのcodegen/ベンチマーク/PA呼び出しでpwdをsessionDirに設定してください。設定しない場合、コンパイル済みMEXとSILバイナリがユーザーの作業ディレクトリに作成されます。

このスキルが呼び出す2つのヘルパー、benchmarkMexとextractDiagnosticsは、このスキルのSKILL.mdがあるフォルダー内の/スクリプトサブフォルダーにあります。MATLAB関数を名前で呼び出せるのは、そのフォルダーがパス上にある場合のみです。そのため、セットアップで/スクリプトサブフォルダーをパスに追加し、クリーンアップ時に削除してください。その後、ヘルパーは名前のみで呼び出します(benchmarkMex(...)、extractDiagnostics(...))。

% TEMPLATE — not executable
sessionDir = fullfile(tempdir, "gpu_opt_" + string(datetime("now", Format="yyyyMMdd_HHmmss")));
mkdir(sessionDir);
scriptsDir = fullfile("<skill_dir>", "scripts");   % this skill's scripts/ folder (absolute)
addedDir = fileparts(which("<designFile>"));
addpath(scriptsDir, addedDir);
oldDir = cd(sessionDir);
cleanupCd = onCleanup(@() (cd(oldDir), rmpath(scriptsDir), rmpath(addedDir)));   %#ok<NASGU>

すべてのアーティファクトはsessionDirのみに保存し、ユーザーの作業ディレクトリには決して保存しないでください。

ステップ 1 — 元のコードでのコード生成

変更していない設計ファイルに対して codegen を実行し、実際に失敗する箇所を確認します。 どの関数がサポートされていないかを推測せず、コンパイラーに判断させてください。

1a. コード生成の構成を選択します。 ユーザーが指定した構成を使用します。指定がない場合は、MEX をデフォルトとして使用します。

cfg = coder.gpuConfig("mex");  % default — replace if user specifies a config

サポート対象: mex、lib、dll。exe ターゲットはこのスキルではサポートされていないため、ステップ 2~5 ではベンチマークや等価性の検証を行えません。ユーザーが coder.gpuConfig("exe") を指定した場合は、処理を中止し、次のいずれかに切り替えるよう依頼してください。

  • 最適化ワークフローで使用するために mex に切り替える(推奨 — 最も速く反復できます)、または
  • 配布可能な成果物が必要な場合は lib/dll に切り替える(スキルによって SIL が有効になり、生成した MEX でベンチマークできます)。

最適化が完了したら、最終最適化済みソースから exe を再生成できます。

1b. lib/dll 構成の場合:SIL を有効にします。 これは必須です。SIL がないと呼び出し可能な MEX が存在せず、ステップ 2~5 でベンチマークを行えません。codegen を呼び出す前に設定してください。

% TEMPLATE — not executable
if ~isa(cfg, 'coder.MexCodeConfig')
    cfg.VerificationMode = 'SIL';   % required for benchmarking lib/dll targets
end

SIL が失敗した場合、または利用できない場合(Embedded Coder ライセンスがない場合など)は、その旨を報告して処理を中止してください。ベンチマークを黙って省略してはいけません。

1c. 入力を解決します。 ユーザーが具体的な入力値を指定した場合は、そのまま使用します。型/サイズのみを指定した場合(例:「サイズ 1024x1 の double ベクトル 2 つ」)は、仕様に合った入力を生成します。生成した内容(型、サイズ、場所、生成方法)を正確に記録し、最終レポートに記載できるようにしてください。妥当なデフォルトは、浮動小数点数には固定した rng シードでの randn、整数には randi、論理値には rand > 0.5 です。ユーザーが別途指定した場合、または PA によって後から UseGpuInput が指摘された場合を除き、入力は CPU 上に保持してください。ユーザーが値も型/サイズも指定していない場合は、代表的なサイズと型を尋ねてください。codegen -args には具体的なシグネチャが必要であり、入力の形状によってどの最適化が有効かが決まります。

1d. codegen を実行します:

% TEMPLATE — not executable
codegen -config cfg <designFile> -args {<inputs>}

codegen が失敗した場合はエラーを確認し、サポートされていないと報告された箇所のみを修正してください。修正したファイルを sessionDir に <designFile>_v1.m として保存します。

v1 で codegen が正常に成功する必要があります。 成功するまで codegen を再実行してください。これにより、ベースライン MEX <designFile>_v1_mex が生成されます(lib/dll 構成の場合、SIL で生成された MEX も同じ名前で、同様に呼び出せます)。

ステップ 2 — ベースラインのベンチマーク

ステップ 1 で生成した MEX をそのまま使用し、再度 codegen を実行しないでください。収束ベースのヘルパーを使ってベンチマークします。

% TEMPLATE — not executable
baseline = benchmarkMex("<designFile>_v1_mex", {<inputs>});
baselineTime = baseline.MedianTime;
fprintf("Baseline: %.4f ms\n", baselineTime*1000);

ルール:

  • 常に benchmarkMex(または gputimeit)を使用してください。GPU のタイミング測定に tic/toc を使用してはいけません(GPU の演算は非同期です)。
  • benchmarkMex がウォームアップと収束を自動的に処理します。
  • baselineTime を記録してください。すべての改善はこの値を基準に測定します。
ステップ 3 — 構造最適化ループ

最適化を反復して適用します。各反復では、次の操作を行います。

  1. 次の最適化を適用した <designFile>_v<N>.m を sessionDir に作成します
  2. 元のコードと数値的に等価であることを検証します(複数の入力についてはステップ 3b を参照)
  3. sessionDir に対してコード生成を実行します。失敗した場合は修正するか、元に戻します
  4. 新しい MEX を benchmarkMex でベンチマークし、これまでで最良の結果と比較します
  5. 検証に合格した中で最速のバージョンを、現在の最良バージョンとして保持します

MATLAB レベルの再構成パターン(最終的にどの GPU プリミティブを選択するかにかかわらず、コード生成を改善する書き換え):

ソース内のパターン最適化生成される CUDA への効果
ループ変数に依存しない配列値の式がループ内にある(例:w = weights / sum(weights) が各反復で再計算される)式をループの前に移動しますスレッドごとの冗長な計算をなくします。GPU Coder は配列式のループ不変性を常に証明できるとは限らないため、式をカーネルにインライン化し、すべてのスレッドで同じ処理を再計算します。
明示的なループを暗黙的な拡張で置き換えられる(例:B が行ベクトルの場合の for i=1:N, out(i,:) = A(i,:) + B; end)ループを out = A + B に置き換え、暗黙的な拡張を使用しますGPU Coder は暗黙的な拡張に対して、単一の融合カーネルを生成します。明示的なループも並列化される場合がありますが、暗黙的な拡張ではループのオーバーヘッドがなく、より簡潔なカーネルが生成されます。
並列化可能なループ内に分岐があり、実行経路が分岐する(例:条件が要素ごとに予測不可能に変わる if x(i) > 0, a = f(x(i)); else, a = g(x(i)); end)両方の分岐の計算コストが低い場合は、両方を計算し、マスクで選択します(例:a = mask.*f(x) + (~mask).*g(x))分岐する if/else はワープダイバージェンスを引き起こします。同じワープ内のスレッドは各分岐を順に実行します。分岐のないマスクを使えば、すべてのスレッドが同じ命令経路をたどり、ワープのスループットを最大限に戻せます。

上の表で扱っているのは MATLAB レベルの再構成のみで、GPU Coder のプリミティブは網羅していません。最適化を採用する前に、references/gpu-codegen-functions.md を最初から最後まで読み、カーネルプラグマ、並列リダクションとスキャン、アトミック操作、ステンシル、メモリ配置について確認してください。適切なプリミティブは、ループのデータフローの形状(結果の格納先、依存関係の連鎖方法、反復間の競合の有無)によって異なります。表の中で最も似ている行が、正解とは限りません。見た目ではなく、ループのセマンティクスに合わせてください。

このループの終了条件:

  • (ユーザーが目標を指定した場合)パフォーマンス目標を達成している
  • エージェントが特定できる構造最適化がほかにない
  • 構造最適化は最大 5 回までです(ステップ 5 の診断と修正のループには独立した上限 5 回があり、両者の上限は別々に適用されます)
ステップ 3b — 数値的な等価性を検証する

元の関数と最適化後の関数を、最大 5 セットの入力で実行します。まずユーザーの 元の入力を基準として使用し、同じ型とサイズに一致するバリエーションを生成します。 入力型に対応した生成関数を選び(浮動小数点数には randn、整数には randi、 論理値には rand > 0.5)、その型の有効な範囲内に値を保ちます。

% TEMPLATE — not executable — float case; adapt generator to the input type
% Input sets — variants are built from in1, the first input resolved in Step 1c
in1 = <user_or_synthesized_input_from_step_1c>;     % original example
rng(42); in3 = randn(size(in1), 'like', in1);       % random
rng(99); in4 = randn(size(in1), 'like', in1) * 1e6; % large magnitude

エッジケースの入力を含めるのは、この関数で有効な場合に限ります。すべてゼロの セットではほとんど検証できず、関数が入力から導出した値で除算する場合は 退化したケースになります。代わりに、関数に有意な負荷をかけるバリエーションを 選びます。

各出力を個別に比較します。

% TEMPLATE — not executable
outOrig = <designFile>(testInput);
outOpt  = <designFile>_v<N>(testInput);

% Gather gpuArray outputs to CPU for comparison
if isa(outOpt, 'gpuArray'), outOpt = gather(outOpt); end

% If outputs can contain NaN, first check NaN positions match (see NaN rule
% below) — max() ignores NaN, so a corrupted NaN would otherwise pass silently.
denom = max(abs(outOrig(:)));
if denom == 0
    relErr = max(abs(outOpt(:)));  % absolute error when expected is zero
else
    relErr = max(abs(outOrig(:) - outOpt(:))) / denom;
end
fprintf("relErr = %.2e\n", relErr);

出力型ごとの許容誤差:

出力型許容誤差
double1e-6
single1e-3
half1e-2
整数/論理値完全一致(isequal)

ルール:

  • 常に、以前のバージョンではなく、変更を加えていない元の関数と比較します
  • 関数に複数の出力がある場合は、それぞれを出力型に適した許容誤差で確認します
  • 元の関数と最適化後の関数で出力型が異なる場合はバグです。修正してください
  • 相対誤差の正規化には、要素ごとの値ではなく max(abs(expected)) を使用します
  • NaN を処理します。NaN の位置が一致することを確認し(isequal(isnan(a), isnan(b)))、その後、NaN 以外の要素を比較します
  • 入力を gpuArray でラップしてもかまいません(たとえば、UseGpuInput の修正後)。関数の契約は変わりません
ステップ 4 — gpuPerformanceAnalyzer でプロファイリングする

ユーザーがパフォーマンス目標を指定し、ステップ 3 で達成した場合を除き、必ずこのステップを実行してください。 PA では、ベンチマークだけでは検出できない問題(メモリコピー、並列性の低さなど)が明らかになります。

残っているボトルネックを見つけるため、現時点で最良のバージョンで PA を実行します。ユーザーが 最適化対象としているものと同じ対象を PA でプロファイリングできるよう、ステップ 1 と同じ cfg を渡します。

% TEMPLATE — not executable
paDir = fullfile(sessionDir, "pa_v<N>");
mkdir(paDir);

gpuPerformanceAnalyzer("<designFile>_v<N>", {<inputs>}, ...
    Config=cfg, ...
    OutFolder=paDir, ...
    LaunchReport=false);

主な NVP:

  • Config=cfg — ステップ 1 のコード生成設定(ユーザー指定、または デフォルトの coder.gpuConfig("mex"))を再利用します
  • LaunchReport=false — GUI レポートを非表示にします
  • OutFolder — セッションディレクトリ内のサブディレクトリ

mldatx レポートから診断情報を抽出します。

% TEMPLATE — not executable
mldatxFile = fullfile(paDir, "html", "gpuProfiler.mldatx");
results = extractDiagnostics(mldatxFile);

results.numDiagnostics == 0 の場合、プロファイルに問題はありません。「最終レポート」に進みます。

results 構造体には次の情報が含まれます。

  • results.numDiagnostics — 検出された問題の数
  • results.diagnostics(i).id — 名前空間付きの ID(例:"gpucoder:diagnostic:UseGpuInput")
  • results.diagnostics(i).message — 人が読める説明
  • results.diagnostics(i).file — 生成された .cu ファイルへのパス
  • results.diagnostics(i).line — 生成コード内の行番号
ステップ 5 — 診断の修正

results.diagnostics を読み取り、対象を絞って修正します。修正を繰り返すたびに、次の手順を実行します。

  1. sessionDir に <designFile>_v<N+1>.m を作成します
  2. コード生成を実行します。失敗した場合は修正するか、変更を元に戻します
  3. 元の関数との等価性を確認します(ステップ 3b と同じ手順で、同じ入力と同じ許容誤差を使用し、変更していない関数と比較します)
  4. benchmarkMex でベンチマークを行い、現在の最良の結果と比較します

診断 → 修正の対応表:

診断 ID修正
gpucoder:diagnostic:NoKernelFunPragma指定された関数にカーネルプラグマがないため、カーネルが生成されていません。指定された関数に coder.gpu.kernelfun を追加します。
gpucoder:diagnostic:LargeLocalMemoryUsagePerThreadカーネルがスレッドごとに大量のローカルメモリを使用しています。ループ本体の式を簡素化します。複合式を少ないステップに分割する、大きな一時配列をループの外に取り出す、または中間変数の数を減らして、スレッドごとのレジスター/ローカルメモリの負荷を軽減します。
gpucoder:diagnostic:UseGpuInput指摘された入力は GPU 上で最初に使用されるため、CPU から GPU へのメモリーコピーが発生しています。コード生成/PA に渡す前に、指摘された入力のみを gpuArray に変換します。他の入力を推測で変換しないでください。
gpucoder:diagnostic:KernelDiagnosticsNotEnoughParallelismカーネルが起動するスレッド数が少なすぎます。各スレッドの処理量が多く、並列性が低くなっています。独立した反復の数を増やします。並列化するループがより多くの要素を処理するように計算を再構成する、逐次的な複数ステップの処理を個別のベクトル化操作に分割する、またはデータをより多くの独立したチャンクに分割します。
gpucoder:diagnostic:LongCPULoopHasLargeMemcpyCPU で実行されるループにより、反復のたびに GPU と CPU の同期が発生しています。ループ内で GPU の結果を CPU に読み戻さないように再構成します。ループが完了するまで中間結果を GPU 上に保持します。ループ本体がカーネルを起動する場合は、外側のループを並列化するか、内側の GPU 処理と統合します。
gpucoder:diagnostic:LongRunningLoopLargeIteration反復回数の多いループが実行時間の大半を占めていますが、並列化されていません。ループ本体をベクトル化するか、反復間に依存関係がない形に再構成します。依存関係が本当にない場合は、coder.gpu.kernelfun または coder.gpu.kernel によってカーネルに割り当てられます。
gpucoder:diagnostic:KernelLaunchOverheadLargeInLoop並列化された内側のループが、オーバーヘッドの大きい単純なカーネルを多数起動しています。内側のループではなく、外側(親)のループを並列化します。これにより、多数の小さなカーネル起動を、より少数の大きなカーネルに統合できます。コンパイラーが自動的に並列化しない場合は、外側のループに coder.gpu.kernel を使用します。
gpucoder:diagnostic:MemoryDiagnosticsRepeatedMemoryCopyInsideLoop変数が反復ごとに CPU と GPU の間でコピーされ、同期が発生しています。変数の割り当てと初期化をループの外に移動します。変数が GPU 上で書き込まれ、各反復で CPU から読み取られる(またはその逆)場合は、ループの実行中、変数が一方のデバイス上にとどまるよう再構成します。
gpucoder:diagnostic:LongRunningLoopUnknownReasonCPU で実行されるループが、原因不明ながら大きな実行時間を占めています。ループ本体に逐次的な依存関係がないか調査します。依存関係がない場合は、ベクトル化するか coder.gpu.kernelfun を追加します。依存関係がある場合は、依存する部分を独立した部分から分離できるか確認します。ループ内でカーネルを起動する関数を呼び出している場合、外側のループの並列化を妨げるため、それらの呼び出しをインライン化するか、再構成します。

反復回数の上限: 診断の修正は 5 ラウンドで終了します(ステップ 3 の構造的な反復回数の上限とは別です)。解決できない問題が残っている場合は報告します。

ステップ 6 — 最終レポート

最良の結果を提示します。

# GPU Codegen Optimization Results

## Files
- Original: <designFile>.m
- Best version: <designFile>_v<N>.m
- Session directory: <sessionDir>

## Inputs Used
| Arg | Type | Size | Location | Source |
|-----|------|------|----------|--------|
| in1 | double | 1024x1 | CPU | user-provided / synthesized via `randn(1024,1)` with `rng(0)` |
| ... | ... | ... | ... | ... |

State "user-provided" when the user gave concrete values, or describe the
generator (function + seed) when inputs were synthesized. Reported timings
below are reproducible only with these inputs.

## Performance
| Version | Time (ms) | vs Baseline |
|---------|-----------|-------------|
| Baseline | X.XX | — |
| v2  | X.XX | Y% faster |
| v<N> | X.XX | Z% faster |

## Diagnostics Resolved
| Diagnostic | Status |
|---|---|
| <issue> | RESOLVED |
| <issue> | OPEN (reason) |

## Changes Applied
1. <change description>
2. <change description>

## Numerical Verification
All versions verified equivalent (max relErr below the type-appropriate
tolerance) across N test inputs.

最終成果物は <designFile>_v<N>.m です。等価性チェックに合格した中で最も高い性能を示すバージョンです。

規約

  • 元の設計ファイルは上書きしないでください。必ず _v<N> のバリアントを作成してください
  • すべての成果物は1つのセッション tempdir に保存してください。ユーザーの作業ディレクトリには保存しないでください
  • すべての計測に benchmarkMex(または gputimeit)を使用してください。GPU コードには tic/toc を使用しないでください
  • まずコード生成を実行してエラーを確認してください。関数のサポート状況を推測しないでください
  • 変更のたびに、複数の多様な入力を使って同等性を検証してください
  • ユーザーが指定したコード生成設定を使用してください。指定がない場合は coder.gpuConfig("mex") をデフォルトにしてください
  • 最適化 → ベンチマーク → 繰り返しのサイクルを続けてください。1回で終えると、性能を十分に引き出せません
  • プロファイリングで CPU→GPU コピーが確認された入力のみ gpuArray に変換してください。すべての入力を推測で変換しないでください

著作権 2026 The MathWorks, Inc.


コメント