AWS Graviton4でARM Performance Librariesを使ってOpenBLASと性能比較してみた
はじめに
前回の検証では、Graviton4 向けに OpenBLAS をソースビルドし、apt でインストール版と比べ約 8〜10% の性能向上を確認しました。ARM 社公式の高性能数学ライブラリ ARMPL を利用した場合はどうなのか気になったので検証してみました。
検証結果早見
単純な行列計算の時間の比較では、ARMPL は apt インストール版の OpenBLAS より 31〜41% 高速でした。OpenBLAS Graviton4 最適化版との比較でも 20〜31% の性能向上を確認できました。早い!
ARMPL とは
ARM Performance Libraries(ARMPL)は、ARM 社が提供する高性能な数学ライブラリです。
- BLAS(基本線形代数サブプログラム)
- LAPACK(線形代数パッケージ)
- FFT(高速フーリエ変換)
- libm(数学関数ライブラリ)
無償ライセンス
商用利用・製品への組み込み・条件付きで再配布が可能ですが、保証・サポートはありません。パフォーマンをレポートすること自体も問題なかったので検証することにしました。
"Your Reports" means any written reports or other information relating to the behavior or performance of Your Software or Your Hardware, in html, binary, text or any other format, generated by you from or using the Arm Tools and any modifications thereto.
license_agreement.txt より
検証環境
| 項目 | バージョンなど |
|---|---|
| インスタンスタイプ | m8g.xlarge(4 vCPU, 16 GiB) |
| OS | Ubuntu 24.04.3 LTS |
| CPU | AWS Graviton4(Neoverse-V2) |
| GCC | 13.3.0 |
| OpenBLAS(apt) | 0.3.26 |
| OpenBLAS(Graviton4 最適化) | 0.3.31.dev |
| ARMPL | 26.01 |
| リージョン | us-east-1 |
前回検証で構築した環境をそのまま使用しています。
検証手順
ARMPL のインストール
ARM 社の公式サイトからパッケージを取得します。
ちょうど今月リリースされた最新版があったのでそちらをダウンロードします。
cd ~
wget https://developer.arm.com/-/cdn-downloads/permalink/Arm-Performance-Libraries/Version_26.01/arm-performance-libraries_26.01_deb_gcc.tar
展開してインストールします。-a オプションはライセンス同意を自動で承諾します。
tar -xvf arm-performance-libraries_26.01_deb_gcc.tar
cd arm-performance-libraries_26.01_deb/
sudo ./arm-performance-libraries_26.01_deb.sh -a
Unpacking...
Setting up arm-performance-libraries-26.01-gcc (26.01-1333) ...
Setting up armpl-26.01-gcc (26.01.0-5095) ...
The environment-modules package does not appear to be installed.
インストール先を確認します。
ls /opt/arm/
arm-performance-libraries_26.01_gcc armpl_26.01_gcc modulefiles
ライブラリファイルを確認します。
ls /opt/arm/armpl_26.01_gcc/lib/ | head -5
libamath.a
libamath.so
libamath_repro.a
libamath_repro.so
libarmpl.a
主要ファイルは以下のとおりです。
libarmpl.so:シリアル版(シングルスレッド)libarmpl_mp.so:OpenMP 版(マルチスレッド対応)
ベンチマークプログラムの準備
前回検証と同じ DGEMM ベンチマークを使用します。ソースコードは同一で、リンクするライブラリのみを差し替えてビルドします。
mkdir -p ~/benchmark-armpl
cd ~/benchmark-armpl
#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <cblas.h>
int main(int argc, char *argv[]) {
int N = (argc > 1) ? std::stoi(argv[1]) : 10000;
int REPEAT = (argc > 2) ? std::stoi(argv[2]) : 3;
// 行列メモリ確保
std::vector<double> A(N * N);
std::vector<double> B(N * N);
std::vector<double> C(N * N, 0.0);
// 乱数初期化
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<double> dist(0.0, 1.0);
for (int i = 0; i < N * N; ++i) {
A[i] = dist(gen);
B[i] = dist(gen);
}
std::cout << "Matrix size: " << N << " x " << N << std::endl;
std::cout << "Repeat: " << REPEAT << " times" << std::endl;
// ベンチマーク実行
auto start = std::chrono::high_resolution_clock::now();
for (int r = 0; r < REPEAT; ++r) {
cblas_dgemm(CblasRowMajor, CblasNoTrans, CblasNoTrans,
N, N, N, 1.0, A.data(), N, B.data(), N, 0.0, C.data(), N);
}
auto end = std::chrono::high_resolution_clock::now();
auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
// 結果出力
double elapsed_sec = elapsed_ms / 1000.0;
double gflops = (2.0 * N * N * N * REPEAT) / elapsed_sec / 1e9;
std::cout << "Time: " << elapsed_ms << " ms" << std::endl;
std::cout << "GFLOPS: " << gflops << std::endl;
return 0;
}
各ライブラリ向けビルド
同一ソースコードから 3 種類のバイナリをビルドします。
OpenBLAS apt 版のビルドです。
g++ -O3 benchmark_dgemm.cpp -o bench_openblas_apt -lopenblas
OpenBLAS Graviton4 最適化版のビルドです。
g++ -O3 benchmark_dgemm.cpp -o bench_openblas_neoversev2 \
-I/opt/openblas-neoversev2/include \
-L/opt/openblas-neoversev2/lib -lopenblas \
-Wl,-rpath,/opt/openblas-neoversev2/lib
ARMPL 版のビルドです。OpenMP 版(libarmpl_mp.so)をリンクします。
g++ -O3 benchmark_dgemm.cpp -o bench_armpl_mp \
-I/opt/arm/armpl_26.01_gcc/include \
-L/opt/arm/armpl_26.01_gcc/lib -larmpl_mp -lm -fopenmp \
-Wl,-rpath,/opt/arm/armpl_26.01_gcc/lib
リンク先を確認します。
ldd ./bench_openblas_apt | grep -E "openblas|armpl"
libopenblas.so.0 => /lib/aarch64-linux-gnu/libopenblas.so.0 (0x0000f2b4b3090000)
ldd ./bench_openblas_neoversev2 | grep -E "openblas|armpl"
libopenblas.so.0 => /opt/openblas-neoversev2/lib/libopenblas.so.0 (0x0000faea603c0000)
ldd ./bench_armpl_mp | grep -E "openblas|armpl"
libarmpl_mp.so => /opt/arm/armpl_26.01_gcc/lib/libarmpl_mp.so (0x0000f165cfbf0000)
ソースコードを変更せずにライブラリを差し替えできることを確認しました。
ベンチマーク実行
10000 x 10000 の行列積を 3 回実行しました。
OpenBLAS apt 版
# 1スレッド
export OMP_NUM_THREADS=1
./bench_openblas_apt 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 238419 ms
GFLOPS: 25.1658
# 4スレッド
export OMP_NUM_THREADS=4
./bench_openblas_apt 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 79173 ms
GFLOPS: 75.7834
OpenBLAS Graviton4 最適化版
# 1スレッド
export OMP_NUM_THREADS=1
./bench_openblas_neoversev2 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 217550 ms
GFLOPS: 27.5799
# 4スレッド
export OMP_NUM_THREADS=4
./bench_openblas_neoversev2 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 73311 ms
GFLOPS: 81.8431
ARMPL
# 1スレッド
export OMP_NUM_THREADS=1
./bench_armpl_mp 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 181214 ms
GFLOPS: 33.11
# 4スレッド
export OMP_NUM_THREADS=4
./bench_armpl_mp 10000 3
Matrix size: 10000 x 10000
Repeat: 3 times
Time: 55981 ms
GFLOPS: 107.179
実行結果と分析
想像よりずっと早い。
結果まとめ
| ライブラリ | スレッド数 | 時間 | GFLOPS | apt 比 |
|---|---|---|---|---|
| OpenBLAS(apt) | 1 | 238 秒 | 25.17 | - |
| OpenBLAS(apt) | 4 | 79 秒 | 75.78 | - |
| OpenBLAS(Graviton4 最適化) | 1 | 218 秒 | 27.58 | +9.6% |
| OpenBLAS(Graviton4 最適化) | 4 | 73 秒 | 81.84 | +8.0% |
| ARMPL | 1 | 181 秒 | 33.11 | +31.5% |
| ARMPL | 4 | 56 秒 | 107.18 | +41.4% |
性能比較
apt でインストール OpenBLAS vs OpenBLAS Graviton4 最適化
前回検証と同様の結果を確認しました。Graviton4 向けにソースビルドすることで約 8〜10% の性能向上が得られました。
理由は以下のとおりです。
- apt 版は
DYNAMIC_ARCHでビルドされており、実行時にneoversev1カーネルを使用 - Graviton4 最適化版はビルド時に明示的に
neoversev2カーネルを使用
OpenBLAS Graviton4 最適化 vs ARMPL
ARMPL は OpenBLAS より大幅に高速でした。
| スレッド数 | OpenBLAS(Graviton4 最適化) | ARMPL | 差 |
|---|---|---|---|
| 1 | 27.58 GFLOPS | 33.11 GFLOPS | +20.1% |
| 4 | 81.84 GFLOPS | 107.18 GFLOPS | +31.0% |
apt 版 OpenBLAS との比較では 31〜41% の性能向上です。ARMPL は ARM 社が Neoverse 向けに最適化しているため高いパフォーマンスを発揮しているようです。ここまで違いがあるとは思っていませんでした。
マルチスレッドスケーリング
ARMPL はスケーリング効率も良好です。4 コアまでしか試していないため参考程度に。
| ライブラリ | 1→4 スレッド倍率 |
|---|---|
| OpenBLAS(apt) | 3.01 倍 |
| OpenBLAS(Graviton4 最適化) | 2.97 倍 |
| ARMPL | 3.24 倍 |
まとめ
Graviton4 環境で OpenBLAS と ARMPL の性能を比較しました。
- ARMPL は apt 版 OpenBLAS より 31〜41% 高速
- ARMPL は OpenBLAS Graviton4 最適化版より 20〜31% 高速
- ソースコード変更なしでライブラリを差し替え可能
Graviton4 で数値計算性能を最大化するなら ARMPL の採用検討の余地ありでした。
おわりに
ARMPL は OpenBLAS より大幅に高速でした。BLAS 標準 API のため既存コードの変更も不要でした。もっとめんどくさいことになるものだと勝手に思っていました。ということで x86-64 から Graviton へ移行する際は ARMPL を検討する価値がありました。
ARMPL 利用時の注意点
ARMPL にはシリアル版(libarmpl.so)と OpenMP 版(libarmpl_mp.so)があります。通常マルチスレッドで使用しますので OpenMP 版をリンクしてください。
今回の検証で最初にシリアル版をリンクしてしまい、スレッド数を増やしても性能が変わらなく間違いに気が付きました。
# シリアル版(libarmpl.so)をリンクした場合
export OMP_NUM_THREADS=1
./bench_armpl 10000 3
# Time: 181176 ms, GFLOPS: 33.117
export OMP_NUM_THREADS=4
./bench_armpl 10000 3
# Time: 181121 ms, GFLOPS: 33.127 ← 性能が変わらない!
スレッド数を 1 から 4 に増やしても実行時間がほぼ同じです。OMP_NUM_THREADS を変更しても効果がない場合はリンクしているライブラリを確認してください。
ldd ./bench_armpl | grep armpl
# libarmpl.so が表示される → シリアル版
ldd ./bench_armpl_mp | grep armpl
# libarmpl_mp.so が表示される → OpenMP版(正しい)







