シリーズ: Apple Silicon AI技術スタック 完全解説
難易度: ★★★☆☆(中級)
想定読者: パフォーマンスの仕組みを深く理解したい人、Metal統合をしたい人
TL;DR
- MPSGraphは計算グラフを構築・最適化・実行するフレームワーク
- Core MLやTensorFlowのGPUアクセラレーションを裏で支えている
- 「ステッチング」技術で複数の演算を融合し、高速化を実現
- 通常は直接使わないが、仕組みを理解するとパフォーマンスチューニングに役立つ
MPSGraphとは?
これまでMPSとMLXを見てきた。でも、その裏で動いているものがある。それがMPSGraph。
Apple公式ドキュメントでは:
"Build, compile, and execute compute graphs utilizing all the different compute devices on the platform, including GPU, CPU, and Neural Engine."
(GPU、CPU、Neural Engineを含むプラットフォーム上のすべての異なる計算デバイスを活用して、計算グラフを構築、コンパイル、実行します)
出典:Apple Developer Documentation - Metal Performance Shaders Graph
WWDC24のセッションでは、もう少し詳しく説明されている:
"Metal Performance Shaders Graph, which is a framework for constructing and running general purpose compute graphs using Metal. MPSGraph provides low level control over GPU synchronization and memory, so in some cases you may want to use MPSGraph directly."
(Metal Performance Shaders Graphは、Metalを使用して汎用計算グラフを構築・実行するためのフレームワークです。MPSGraphはGPU同期とメモリに対する低レベル制御を提供するため、場合によってはMPSGraphを直接使用することもできます)
出典:WWDC24 - Accelerate machine learning with Metal
計算グラフって何?
機械学習に慣れた人なら「計算グラフ」はお馴染みだろう。でも念のため説明しておく。
入力テンソル A → 行列乗算 → 活性化関数 → 出力テンソル B
↑
入力テンソル C
こんな風に、演算を「ノード」、データの流れを「エッジ」としてグラフ構造で表現したもの。PyTorchやTensorFlowの内部でも使われている概念だ。
なぜグラフで表現するのか?
- 最適化の機会が見える - 全体を見て無駄を省ける
- 並列化しやすい - 依存関係が明確
- 自動微分が簡単 - グラフを逆向きにたどればいい
- 異なるハードウェアへの振り分け - CPU/GPU/ANEの最適な割り当て
MPSGraphは、このグラフをApple Silicon上で最も効率的に実行する方法を見つけ出す。
ステッチング:MPSGraphの秘密兵器
MPSGraphが高速な理由、それが「ステッチング」だ。
WWDC20のセッションで詳しく説明されている:
"Usually, for every operation, output would be written out to memory. The next operation would read from this same global memory, and this would end up causing unneeded memory traffic and performance issues, especially on the GPU. However, MPSGraph compiler applies a special optimization to automatically fuse such operations for the users. We call this stitching. The MPSGraph compiler recognizes all adjacent operations and passes it to the Metal compiler. The Metal compiler fuses the operations together to create a single optimized Metal shader."
(通常、各演算ごとに出力がメモリに書き出されます。次の演算は同じグローバルメモリから読み込むため、不要なメモリトラフィックとパフォーマンス問題が発生します。しかしMPSGraphコンパイラは、自動的にそれらの演算を融合する特別な最適化を適用します。これをステッチングと呼びます。MPSGraphコンパイラは隣接するすべての演算を認識し、Metalコンパイラに渡します。Metalコンパイラはそれらを融合して単一の最適化されたMetalシェーダーを作成します)
出典:WWDC20 - Build customized ML models with the Metal Performance Shaders Graph
図で理解するステッチング
【最適化前】
Op1
↓ メモリ書き込み(遅い)
Global Memory
↓ メモリ読み込み(遅い)
Op2
↓ メモリ書き込み
Global Memory
↓ メモリ読み込み
Op3
【ステッチング後】
Op1 → Op2 → Op3
(全部レジスタ上で完結!メモリアクセス最小化)
GPUの処理でボトルネックになりがちな「メモリ帯域」を劇的に削減できる。これがMPSGraphの高速化の秘密だ。
Core MLとTensorFlowを支えるMPSGraph
実は、あなたがCore MLやTensorFlow-Metalを使っているとき、裏ではMPSGraphが動いている。
WWDC21のセッションでは:
"The MPSGraph framework has been adopted by higher level machine learning frameworks like Core ML and TensorFlow for GPU acceleration. This year, we have optimized MPSGraph even further with a combination of kernel improvements and stitching adoption."
(MPSGraphフレームワークは、Core MLやTensorFlowなどの高レベル機械学習フレームワークにGPUアクセラレーションのために採用されています。今年、カーネル改善とステッチングの採用の組み合わせで、MPSGraphをさらに最適化しました)
出典:WWDC21 - Accelerate machine learning with Metal Performance Shaders Graph
技術スタックの関係
[Core ML] [TensorFlow-Metal] [PyTorch MPS] [MLX]
↓
[MPSGraph] ← 計算グラフの最適化
↓
[MPS] ← 最適化されたカーネル
↓
[Metal] ← GPU API
↓
[Apple GPU]
MPSGraphは「翻訳者」のような存在。高レベルフレームワークの計算グラフを受け取り、Apple GPUで最も効率的に実行する形に変換する。
TensorFlow-Metalのインストール
TensorFlowでMPSGraphの恩恵を受けるのは簡単:
pip install tensorflow-macos
pip install tensorflow-metal
これだけで、TensorFlowの演算がMPSGraph経由でGPUアクセラレーションされる。
import tensorflow as tf
# GPUが認識されているか確認
print(tf.config.list_physical_devices('GPU'))
# [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]
# 以降は通常通りTensorFlowを使う
model = tf.keras.Sequential([...])
model.fit(x_train, y_train) # GPU加速される
MPSGraphを直接使う場面
普通の開発者がMPSGraphを直接触ることは少ないかもしれない。でも、こんな場面では有用:
"For example, if your application uses Metal, you can use MPSGraph to sequence ML tasks with other GPU work. You can also share low level Metal resources such as buffers."
(例えば、アプリケーションがMetalを使用している場合、MPSGraphを使ってMLタスクを他のGPU作業と順序付けることができます。バッファなどの低レベルMetalリソースを共有することもできます)
出典:WWDC24 - Accelerate machine learning with Metal
直接使うべき場面
- ゲームエンジン開発 - レンダリングとMLを密に統合
- カスタムレンダラー - グラフィックスパイプラインにMLを組み込む
- 極限の最適化 - フレームワークの抽象化を排除したい
- Metal既存コードとの統合 - バッファ共有でコピー削減
Swiftでの基本的な使用例
import MetalPerformanceShadersGraph
// グラフを作成
let graph = MPSGraph()
// 入力テンソルを定義(プレースホルダー)
let inputA = graph.placeholder(
shape: [2, 3],
dataType: .float32,
name: "inputA"
)
let inputB = graph.placeholder(
shape: [3, 4],
dataType: .float32,
name: "inputB"
)
// 行列乗算を追加
let matmul = graph.matrixMultiplication(
primary: inputA,
secondary: inputB,
name: "matmul"
)
// 活性化関数を追加(ステッチングで融合される)
let relu = graph.reLU(with: matmul, name: "relu")
// 実行のためのデバイスとキュー
let device = MTLCreateSystemDefaultDevice()!
let commandQueue = device.makeCommandQueue()!
// 入力データを準備
let dataA: [Float] = [1, 2, 3, 4, 5, 6] // 2x3
let dataB: [Float] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] // 3x4
// MPSGraphTensorDataを作成
let tensorDataA = MPSGraphTensorData(
device: MPSGraphDevice(mtlDevice: device),
data: Data(bytes: dataA, count: dataA.count * MemoryLayout<Float>.size),
shape: [2, 3],
dataType: .float32
)
// グラフを実行
let results = graph.run(
with: commandQueue,
feeds: [inputA: tensorDataA, inputB: tensorDataB],
targetTensors: [relu],
targetOperations: nil
)
// 結果を取得
let outputData = results[relu]!
WWDC24の新機能:Transformer向け最適化
2024年、MPSGraphはTransformerモデル向けに大幅強化された:
"MPS and MPSGraph have new features which enable you to improve your transformer models. These improvements fall into three categories. First, improved compute performance. Next, memory bandwidth savings. And finally, quality improvements for transformer models."
(MPSとMPSGraphには、Transformerモデルを改善できる新機能があります。これらの改善は3つのカテゴリに分類されます。まず計算パフォーマンスの向上、次にメモリ帯域の節約、最後にTransformerモデルの品質向上です)
出典:WWDC24 - Accelerate machine learning with Metal
Scaled Dot-Product Attention (SDPA)
特に重要なのが、SDPA(Scaled Dot-Product Attention)のネイティブサポート:
"MPSGraph now has an operation which combines this sequence of operations into a single kernel which executes more efficiently. To use this operation, call the scaledDotProductAttention method on an MPSGraph object."
(MPSGraphには、この一連の演算を単一のより効率的に実行されるカーネルに結合する演算が追加されました。この演算を使用するには、MPSGraphオブジェクトのscaledDotProductAttentionメソッドを呼び出します)
Transformerの心臓部であるAttention機構が、一発の最適化されたカーネルで実行できる。
// Transformerのアテンション計算
let attention = graph.scaledDotProductAttention(
query: queryTensor,
key: keyTensor,
value: valueTensor,
scale: Float(1.0 / sqrt(Float(headDim))),
name: "sdpa"
)
KVキャッシュサポート
LLMの推論を高速化するKVキャッシュも効率的に扱える:
// キャッシュされたKey/Valueを使用
let cachedAttention = graph.scaledDotProductAttention(
query: newQueryTensor,
key: cachedKeyTensor, // 前のトークンのキー
value: cachedValueTensor, // 前のトークンのバリュー
scale: scale,
name: "cached_sdpa"
)
MPSGraph Viewer:Xcode 16の新ツール
モデルの最適化状況を可視化したい?Xcode 16で新しいツールが登場した:
"Today, I'm excited to introduce the newest addition to the Metal tools, coming in Xcode 16, the MPSGraph Viewer! It's a brand new tool, specifically designed for machine learning and AI. Now, you can directly open MPSGraph packages in Xcode, and visualize how your operations are connected."
(Xcode 16に新しく追加されるMPSGraph Viewerを紹介します!機械学習とAI専用に設計された全く新しいツールです。XcodeでMPSGraphパッケージを直接開き、演算がどのように接続されているかを可視化できます)
出典:WWDC24 - Accelerate machine learning with Metal
mpsgraphtoolでの変換
ONNXモデルからMPSGraphパッケージへの変換:
# ONNXモデルをMPSGraphパッケージに変換
mpsgraphtool convert model.onnx -o model.mpsgraphpackage
ビューアの機能
- グラフ構造の可視化 - ノードとエッジの関係を図示
- デバイス最適化の確認 - 特定のデバイスでどう最適化されるか
- ステッチングの確認 - どの演算が融合されているか
- ボトルネックの特定 - パフォーマンス問題の発見
MPSGraphPackage:シリアライゼーション
グラフをファイルに保存し、起動時間を短縮できる:
// グラフをパッケージとして保存
let serializationDescriptor = MPSGraphSerializationDescriptor()
let executable = // コンパイル済みのMPSGraphExecutable
try executable.serialize(
descriptor: serializationDescriptor,
to: packagePath
)
// 後でロード
let compilationDescriptor = MPSGraphCompilationDescriptor()
let loadedExecutable = try MPSGraphExecutable(
url: packagePath,
options: compilationDescriptor
)
複雑なグラフの場合、事前コンパイルで起動時間を大幅に短縮できる。
パフォーマンスの考え方
MPSGraphを理解すると、パフォーマンスチューニングの考え方が変わる:
良いパターン
✅ 連続した演算をまとめて投入
→ ステッチングの機会が増える
✅ 大きなバッチサイズ
→ GPU利用効率が上がる
✅ 同じ形状の繰り返し計算
→ コンパイル済みグラフを再利用
悪いパターン
❌ 細かい演算を逐次実行
→ ステッチングできない
❌ 頻繁な形状変更
→ 再コンパイルのオーバーヘッド
❌ 小さすぎるテンソル
→ GPU起動オーバーヘッドが支配的
まとめ:MPSGraphは「縁の下の力持ち」
MPSGraphを直接使う機会は少ないかもしれない。でも:
- Core MLでモデルを動かすとき
- TensorFlow-Metalでトレーニングするとき
- MLXでLLMを推論するとき
裏ではMPSGraphが働いている。計算グラフの最適化、ステッチングによる演算融合、適切なデバイス(GPU/CPU/Neural Engine)への振り分け。
Apple Siliconの機械学習パフォーマンスを支える、まさに縁の下の力持ちだ。
次に読む
- Metal基礎解説 - MPSGraphの下で動くGPU API
- MPS基礎解説 - MPSGraphが使うカーネル層
- Core ML基礎解説 - MPSGraphを活用する高レベルフレームワーク
- シリーズ目次に戻る
参考文献
- Apple Developer Documentation - Metal Performance Shaders Graph
- WWDC24 - Accelerate machine learning with Metal
- WWDC21 - Accelerate machine learning with Metal Performance Shaders Graph
- WWDC20 - Build customized ML models with the Metal Performance Shaders Graph
- Apple Developer Documentation - MPSGraph