プログラミング
85.3 GFlops: AMD Zen 3コア単体でのFP32行列乗算の最適化
85.3 GFlops: Optimizing FP32 Matrix Multiplication on a Single AMD Zen 3 Core (github.com)
要約
本記事は、AMD Zen 3 CPUの単一コア上でFP32行列乗算(GEMM)のパフォーマンスを最大化するための詳細な最適化手法について解説しています。AVX2/FMA命令セットを活用し、キャッシュブロッキング、レジスタブロッキング、FMAチェイニング、パッキング戦略などを組み合わせることで、理論上のピーク性能の63.5%にあたる85.30 GFLOPSを達成しました。この成果は、既存の最適化ライブラリに匹敵する性能であり、低レベルのCPU最適化の可能性を示しています。
全文翻訳
85.30 GFLOPS Single‑Core FP32 GEMM on AMD Zen 3
AVX2/FMAのC++イントリンシックを用いた行列乗算の体系的な最適化探求。AMD Ryzen 5 5500上で理論上のピーク134.4 GFLOPSの63.5%を達成しました。
概要
このリポジトリには、AMD Zen 3マイクロアーキテクチャの単一コアにおける単精度(FP32)行列乗算(GEMM)の最適化に関する詳細な研究のソースコード、結果、および分析が含まれています。以下の技術を組み合わせた28通りの異なる設定(モデルMX01からMX28)がテストされました。
* 3段階のキャッシュブロッキング(タイリング)(L1, L2, L3)
* レジスタブロッキング(2, 4, 8行)
* FMA命令のチェイニング(chain1からchain6)
* パッキング戦略(パッキングなし、転置、オンザフライB‑pack)
* メモリ配置(32バイト)
* ソフトウェアプリフェッチ
* 非一時ストア(ストリームストア)
最良のモデルであるMX24は、85.30 GFLOPSを維持し、単純な実装を56.5倍上回り、AMD AOCLやOpenBLASのような最適化されたライブラリのパフォーマンスに匹敵しました。
主要な結果
| モデル | 説明 | GFLOPS | % ピーク |
|---|---|---|---|
| MX24 | 4行 + chain4 + B‑pack (BK=256) | 85.30 | 63.5% |
| MX22 | 4行 + chain4 + B‑pack (BK=128) | 84.10 | 62.6% |
| MX23 | 4行 + chain4 + B‑pack (BK=64) | 82.93 | 61.7% |
| MX16 | 4行 + chain4 + 配置済み (パッキングなし) | 72.58 | 54.0% |
| MX20 | 4行 + chain4 + Bᵀ (転置) | 79.21 | 58.9% |
| MX18 | 4行 + chain4 + プリフェッチ | 79.75 | 59.3% |
理論上のピークは、2 FMAポート × 8フロート × 2オペレーション × 4.2 GHz = 134.4 GFLOPSと計算されます。
方法論
評価された最適化技術:
キャッシュブロッキング(タイリング)
BI, BJ, BKを調整し、ブロックがL1(32 KB)、L2(512 KB)、L3(16 MB)キャッシュ内に収まるようにしました。見つかった最良のBKは256で、L2をオーバーフローさせずに再利用を最大化しました。
レジスタブロッキング
CのアキュムレータはYMMレジスタ(16個利用可能)に保持されました。4行が最良のバランスを提供しました。ス spills(追加の16レジスタ)が必要でしたが、Aの再利用がコストを相殺しました。
FMAチェイニング
Zen 3におけるFMA命令のレイテンシは4サイクルです。chain4(4つの独立したアキュムレータ)は、このレイテンシを隠蔽し、2つのFMAポートの最大利用率を達成しました。
パッキング(Packing)
オンザフライB‑pack:Bのブロック(BK×BJ)を連続バッファにコピーし、非連続アクセスを連続アクセスに変換します。完全な転置(L3を汚染する)や直接アクセス(TLBミスに苦しむ)よりも優れています。
配置(Alignment)
_mm_malloc(..., 32) を使用してメモリを配置し、vmovaps(配置済み)命令を使用することで、約5%のパフォーマンス向上が見られました。
プリフェッチング
_mm_prefetchの使用はパフォーマンスを約8%低下させました。これは、Zen 3のハードウェアプリフェッチャーがストリーミングパターンに対して既に効率的であるためです。
非一時ストア
_mm256_stream_psは壊滅的でした(1.24 GFLOPS)。Cは読み取り‑変更‑書き込み操作であり、各書き込みでキャッシュラインが無効化されるためです。
リポジトリ構造
zen3-gemm/
├── README.md
├── src/
│ └── mx85.c # 全28モデル + ベンチマークを含む完全なコード
├── docs/
│ ├── artigo_en.tex # 完全な科学論文(LaTeX)
│ └── resultados/ # ベンチマーク出力ログ
│ ├── mx85_benchmark.txt
│ └── mx85_output8.txt
└── build/
└── Makefile # (オプション) 簡単なコンパイル用
コンパイルと実行
前提条件:
* AVX2およびFMAをサポートするプロセッサ(例: AMD Zen, Intel Haswell以降)
* オペレーティングシステム Windows (10/11) または Linux
* GCC 12.2+ (WindowsではMinGW‑w64) または AVX2イントリンシックをサポートする同等のコンパイラ
* 2048×2048の行列(3つの行列で約48 MB)を格納するのに十分なメモリ
GCC(Windows/MinGWまたはLinux)でのコンパイル:
src/ ディレクトリで以下を実行します:
gcc -O3 -mavx2 -mfma -march=native -funroll-loops -frename-registers -o mx85.exe mx85.c
重要なフラグ:
* -mavx2 -mfma -march=native – SIMD命令を有効にし、現在のCPUに最適化します。
* -funroll-loops – 内部ループを展開します。
* -frename-registers – レジスタ割り当てを改善します(spillsを削減)。
実行:
./mx85.exe
プログラムの動作:
* スレッドアフィニティをコア0(Windows)に設定し、優先度を高くします。
* 各モデルを3回(ウォームアップ)+ 15回測定します。
* 2つの出力ファイルを生成します:
* mx85_benchmark.txt – 完全なランキングと検証。
* mx85_validate.txt – 参照実装との最大誤差。
カスタマイズ:
特定のモデルのみをテストするには、main()関数を変更して直接目的の関数を呼び出すことができます(例: mx24(A, B, C, N))。または、他のサイズの行列の場合は、定数N(約230行目)を変更します。
結果の完全な表
テストされた全28モデル、測定されたGFLOPS、および理論上のピークに対する割合を以下に示します。
Pos | モデル | 説明 | GFLOPS | % ピーク
---|---|---|---|---|
1 | MX24 | 4行 + chain4 + B‑pack BK=256 | 85.30 | 63.5%
2 | MX22 | 4行 + chain4 + B‑pack BK=128 | 84.10 | 62.6%
3 | MX23 | 4行 + chain4 + B‑pack BK=64 | 82.93 | 61.7%
4 | MX25 | 4行 + chain4 + B‑pack + プリフェッチ | 83.15 | 61.9%
5 | MX20 | 4行 + chain4 + Bᵀ (転置) | 79.21 | 58.9%
6 | MX18 | 4行 + chain4 + プリフェッチ | 79.75 | 59.3%
7 | MX17 | 4行 + chain4 + パッキングなし | 78.80 | 58.6%
8 | MX16 | 4行 + chain4 + 配置済み | 72.58 | 54.0%
9 | MX15 | 4行 + chain3 + B‑pack | 62.34 | 46.4%
10 | MX14 | 4行 + chain2 + B‑pack | 55.76 | 41.5%
11 | MX13 | 2行 + chain4 + B‑pack | 53.58 | 39.9%
12 | MX12 | 2行 + chain3 + B‑pack | 50.56 | 37.6%
13 | MX11 | 4行 + chain2 + 配置済み | 47.15 | 35.1%
14 | MX08 | 2行 + chain4 + BK=128 | 45.04 | 33.5%
15 | MX07 | 2行 + chain3 + Btなし | 43.52 | 32.4%
16 | MX05 | 2行 + chain2 + Bt | 41.93 | 31.2%
17 | MX19 | 4行 + chain5 + 配置済み | 42.07 | 31.3%
18 | MX06 | 2行 + chain1 + プリフェッチ | 42.54 | 31.7%
19 | MX04 | 4行 + chain1 + Bt | 40.04 | 29.8%
20 | MX03 | 2行 + chain1 + Bt | 39.34 | 29.3%
21 | MX02 | 2行 + chain1 + j‑block | 37.60 | 28.0%
22 | MX01 | Bt + 2Dtile + 8acc (ベースライン) | 35.16 | 26.2%
23 | MX10 | 4行 + chain6 + Bt | 14.26 | 10.6%
24 | MX26 | 8行 + chain4 + B‑pack | 12.24 | 9.1%
25 | MX28 | 4行 + chain4 + dual16 | 13.75 | 10.2%
26 | MX27 | 4行 + chain4 + C‑in‑regs | 9.13 | 6.8%
27 | MX09 | BI=128, BK=256 + chain4 | 8.38 | 6.2%
28 | MX21 | 4行 + chain4 + ストリームストア | 1.24 | 0.9%
主要な失敗の分析
| モデル | 技術 | GFLOPS | 原因 |
|---|---|---|---|
| MX21 | 非一時ストア | 1.24 | Cは読み取り‑変更‑書き込み操作であり、各ストアでキャッシュが無効化され、DRAMからの再ロードが強制されるため。 |
| MX26 | レジスタ内の8行 | 12.24 | 64個のYMMレジスタが必要で、48個がspilledされ、実行時間を支配するため。 |
| MX27 | k‑block経由のC‑in‑regs | 9.13 | アキュムレータを多数のイテレーションで生かしておき、レジスタ圧力を増加させるため。 |
| MX09 | BI=128, BK=256 | 8.38 | 作業セット(128×256×4 = 128 KB)がL1を超え、多数のミスを引き起こすため。 |
| MX10 | Chain6 | 14.26 | チェーンが長すぎ、レジスタ不足により過度のspillが発生するため。 |
検証
すべてのモデルは、参照実装(単純なijk)に対して検証されました。上位3モデル(MX24, MX22, MX23)は最大絶対誤差=0.0(ビット単位で同一)を示しました。これは、アキュムレーションの順序が保持されているためです。他のモデルは、浮動小数点演算として予想される範囲内で、1e-6程度の誤差を示しました。
引用方法
この作業を研究で利用する場合は、関連論文を引用してください:
@article{housl2025gemm,
title={85.30 GFLOPS Single-Core FP32 Matrix Multiplication on AMD Zen 3},
author={Housl},
journal={arXiv preprint},
year={2025}
}
貢献
貢献を歓迎します!改善、新しいモデル、または他のアーキテクチャへの適応に関するissueやpull requestを自由に提出してください。
ライセンス
このプロジェクトはMITライセンスの下で提供されています。これは、著作権表示と許可を保持している限り、ソフトウェアのコピーを使用、コピー、変更、マージ、公開、配布、サブライセンス、および/または販売できることを意味します。完全な条件については、LICENSEファイルを参照してください。
連絡先と著者情報
著者: Lucas Lima Freitag
E‑mail: freitag.lima@academico.ifrn.edu.br
役割: 著者
所属: Federal University of Rio Grande do Norte (UFRN)
ORCID: 0009-0006-8849-5619
最適化を楽しんでください!🚀
質問、提案、または独自の成果を共有したい場合は、お気軽にご連絡ください。