matlab-optimize-gpu-codegen について
GPU Coder向けに既存のMATLAB設計を反復最適化するスキルです。MEX、ライブラリ、DLLを対象に、コード生成、ベンチマーク、数値等価性の確認、GPU性能診断を行います。
主な機能
- 01GPU Coderのコンパイルワークフロー
- 02生成GPUコードのベンチマーク
- 03数値等価性の検証
- 04GPU性能診断の分析
- 05構造的なコード最適化
活用シーン
- 01既存MATLAB設計から生成するGPU MEXの高速化
- 02GPU Coderのコンパイルエラーの調査
- 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.yamlreferences/gpu-codegen-functions.mdscripts/benchmarkMex.mextractDiagnostics.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 — 構造最適化ループ
最適化を反復して適用します。各反復では、次の操作を行います。
- 次の最適化を適用した
<designFile>_v<N>.mをsessionDirに作成します - 元のコードと数値的に等価であることを検証します(複数の入力についてはステップ 3b を参照)
sessionDirに対してコード生成を実行します。失敗した場合は修正するか、元に戻します- 新しい MEX を
benchmarkMexでベンチマークし、これまでで最良の結果と比較します - 検証に合格した中で最速のバージョンを、現在の最良バージョンとして保持します
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);
出力型ごとの許容誤差:
| 出力型 | 許容誤差 |
|---|---|
double | 1e-6 |
single | 1e-3 |
half | 1e-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 を読み取り、対象を絞って修正します。修正を繰り返すたびに、次の手順を実行します。
sessionDirに<designFile>_v<N+1>.mを作成します- コード生成を実行します。失敗した場合は修正するか、変更を元に戻します
- 元の関数との等価性を確認します(ステップ 3b と同じ手順で、同じ入力と同じ許容誤差を使用し、変更していない関数と比較します)
benchmarkMexでベンチマークを行い、現在の最良の結果と比較します
診断 → 修正の対応表:
| 診断 ID | 修正 |
|---|---|
gpucoder:diagnostic:NoKernelFunPragma | 指定された関数にカーネルプラグマがないため、カーネルが生成されていません。指定された関数に coder.gpu.kernelfun を追加します。 |
gpucoder:diagnostic:LargeLocalMemoryUsagePerThread | カーネルがスレッドごとに大量のローカルメモリを使用しています。ループ本体の式を簡素化します。複合式を少ないステップに分割する、大きな一時配列をループの外に取り出す、または中間変数の数を減らして、スレッドごとのレジスター/ローカルメモリの負荷を軽減します。 |
gpucoder:diagnostic:UseGpuInput | 指摘された入力は GPU 上で最初に使用されるため、CPU から GPU へのメモリーコピーが発生しています。コード生成/PA に渡す前に、指摘された入力のみを gpuArray に変換します。他の入力を推測で変換しないでください。 |
gpucoder:diagnostic:KernelDiagnosticsNotEnoughParallelism | カーネルが起動するスレッド数が少なすぎます。各スレッドの処理量が多く、並列性が低くなっています。独立した反復の数を増やします。並列化するループがより多くの要素を処理するように計算を再構成する、逐次的な複数ステップの処理を個別のベクトル化操作に分割する、またはデータをより多くの独立したチャンクに分割します。 |
gpucoder:diagnostic:LongCPULoopHasLargeMemcpy | CPU で実行されるループにより、反復のたびに 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:LongRunningLoopUnknownReason | CPU で実行されるループが、原因不明ながら大きな実行時間を占めています。ループ本体に逐次的な依存関係がないか調査します。依存関係がない場合は、ベクトル化するか 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.