AMD EPYC (Zen 4) 向けの C++ ビルド環境を作成して OpenBLAS で行列計算を試してみた

AMD EPYC (Zen 4) 向けの C++ ビルド環境を作成して OpenBLAS で行列計算を試してみた

下積み生活しています。
2025.10.31

はじめに

Intel 製ワークステーションで主に行列計算をしているプログラムがあります。このプログラムを AMD の EC2 で動かせるか、最適化できる余地がるのかサンプルプログラムを使って試して学びました。C++ のビルド環境やコンパイラ最適化について、実際に手を動かして理解を深めることが目的です。

背景と目的

第 4 世代 AMD EPYC プロセッサ(Zen 4)でのビルド環境構築と最適化オプションの理解が目的です。Intel 環境からの移行を想定し、以下の点を検証します。

  • AMD EPYC(Zen 4)プロセッサの特徴確認
  • AMD 固有のコンパイラ最適化オプションの効果
  • マルチコアスケーリングの検証

検証内容

c7a インスタンス(第 4 世代 AMD EPYC)で、C++ と OpenBLAS による行列計算を検証しました。環境構築から性能測定、AMD 固有の最適化までの過程を記録します。

結論

AMD EPYC(Zen 4)での OpenBLAS による行列計算では以下が確認できました。

OpenBLAS はコア数に応じてほぼ理想的にスケールしました。シングルコアの実行時間は約 36.8 秒でした。2 コア使用で約 18.3 秒(2.01 倍高速化)、4 コア使用で約 9.2 秒(3.98 倍高速化)となり、ほぼ理論値です。

コンパイラ最適化の効果は、AMD 固有の最適化オプション(-march=native-march=znver3-march=znver4)による性能向上は 1% 未満と限定的でした。計算時間の大部分が事前コンパイル済み OpenBLAS のライブラリで消費されるためです。自前の計算ロジックを多く含むプログラムでは、コンパイラ最適化オプションが効果を発揮する可能性があります。

検証環境

項目 仕様
リージョン ap-northeast-1(東京)
EC2 インスタンス c7a.xlarge(AMD EPYC 9R14 / x86_64 / 4vCPU)
OS Amazon Linux 2023
GCC 11.5.0
Clang 19.1.7
OpenBLAS 0.3.18
CMake 3.22.2

c7a インスタンスは第 4 世代 AMD EPYC プロセッサ(Zen 4)を搭載しています。すべての vCPU が物理コアであり、SMT(Simultaneous Multi-Threading)は使用されていません。物理コア数を重要視をする場合はコスパが良好なインスタンスタイプで、個人的にはオススメです。

https://dev.classmethod.jp/articles/amazon-ec2-m7a-ga/

環境構築

システムアップデート

まずシステムを最新の状態に更新します。

sudo dnf update -y

開発ツールのインストール

C++ コンパイラ、ビルドツール、行列計算ライブラリをインストールします。

# Development Tools グループのインストール
sudo dnf groupinstall -y "Development Tools"

# C++ コンパイラとビルドツール
sudo dnf install -y gcc gcc-c++ cmake make git

# OpenBLAS ライブラリ
sudo dnf install -y openblas-devel lapack-devel

インストール確認

コンパイラバージョンの確認

インストールされたツールのバージョンを確認します。

gcc --version
実行結果
gcc (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
g++ --version
実行結果
g++ (GCC) 11.5.0 20240719 (Red Hat 11.5.0-5)
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
cmake --version
実行結果
cmake version 3.22.2

CMake suite maintained and supported by Kitware (kitware.com/cmake).
make --version
実行結果
GNU Make 4.3
Built for x86_64-amazon-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

OpenBLAS のインストール確認

OpenBLAS パッケージが正しくインストールされているか確認します。

rpm -q openblas openblas-devel
実行結果
openblas-0.3.18-1.amzn2023.0.3.x86_64
openblas-devel-0.3.18-1.amzn2023.0.3.x86_6

AMD EPYC プロセッサの機能確認

CPU アーキテクチャと SIMD 拡張の確認

AMD EPYC 9R14(Zen 4)の SIMD 拡張命令セットを確認します。x86-64 では AVX、AVX2、AVX-512 のサポートしていることが重要です。

SIMD は複数のデータに同じ命令を同時実行する技術です。行列計算のような並列処理では有利です。ちなみに、ARM アーキテクチャでは SVE(Scalable Vector Extension)という異なる SIMD 技術を採用されています。

CPU フラグの確認

cat /proc/cpuinfo | grep flags | head -n 1

AMD EPYC 9R14(Zen 4)は AVX、AVX2、AVX-512 を含む SIMD 拡張命令をサポートしていました。

実行結果
flags		: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm constant_tsc rep_good nopl nonstop_tsc cpuid extd_apicid aperfmperf tsc_known_freq pni pclmulqdq monitor ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt aes xsave avx f16c rdrand hypervisor lahf_lm cmp_legacy cr8_legacy abm sse4a misalignsse 3dnowprefetch topoext perfctr_core invpcid_single ssbd perfmon_v2 ibrs ibpb stibp ibrs_enhanced vmmcall fsgsbase bmi1 avx2 smep bmi2 invpcid avx512f avx512dq rdseed adx smap avx512ifma clflushopt clwb avx512cd sha_ni avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves avx512_bf16 clzero xsaveerptr rdpru wbnoinvd arat avx512vbmi pku ospke avx512_vbmi2 gfni vaes vpclmulqdq avx512_vnni avx512_bitalg avx512_vpopcntdq rdpid flush_l1d

AMD EPYC Zen 4 の AVX-512 実装について

CPU フラグから avx512favx512dqavx512bw などの AVX-512 命令セットがサポートされていることが確認できます。AMD は Zen 4 ではじめて AVX-512 をサポートしました。ただし実装方式は Intel と異なり、ダブルポンプ方式が採用されています。AMD の AVX-512 は 256 ビット幅のデータパスを使用し、512 ビット命令を 2 クロックサイクルで実行します。

  • Intel: 512 ビット幅(1 サイクル)
  • AMD: 256 ビット幅(2 サイクル、ダブルポンプ方式)

CPU 情報

lscpu | grep -E "Architecture|Model name|CPU\(s\)|Thread|Core|Socket|NUMA"
実行結果
Architecture:                            x86_64
CPU(s):                                  4
On-line CPU(s) list:                     0-3
Model name:                              AMD EPYC 9R14
Thread(s) per core:                      1
Core(s) per socket:                      4
Socket(s):                               1
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-3

OpenBLAS ビルド情報の実行時確認

OpenBLAS の x86-64 向け最適化状況を確認するため、情報を表示するプログラムを実行します。前回 Graviton の検証で利用したコードを流用しています。

openblas_info.cpp
#include <stdio.h>

extern "C" {
    char* openblas_get_config(void);
    char* openblas_get_corename(void);
    int openblas_get_parallel(void);
    int openblas_get_num_threads(void);
}

int main() {
    printf("=== OpenBLAS Configuration ===\n");
    printf("%s\n", openblas_get_config());
    printf("\n=== Runtime Information ===\n");
    printf("Core Name: %s\n", openblas_get_corename());
    printf("Parallel: %d\n", openblas_get_parallel());
    printf("Threads: %d\n", openblas_get_num_threads());
    return 0;
}

コンパイルして実行します。

# コンパイル
g++ openblas_info.cpp -o openblas_info -lopenblas

# 実行
./openblas_info
実行結果
=== OpenBLAS Configuration ===
OpenBLAS 0.3.18 DYNAMIC_ARCH NO_AFFINITY Zen SINGLE_THREADED

=== Runtime Information ===
Core Name: Zen
Parallel: 0
Threads: 1

実行結果から、OpenBLAS は AMD EPYC プロセッサを正しく検出し、Zen アーキテクチャ向けに最適化されたコードパスを選択していることが確認できます。

DYNAMIC_ARCH について

OpenBLAS の DYNAMIC_ARCH は実行時に CPU を検出し、最適なコードパスを自動選択します。x86-64 向けビルドには Haswell、Zen、SkylakeX などの複数ターゲットが含まれており、実行環境に応じて最適なコードが使用されます。これは便利。

参考: How to compile against DYNAMIC_ARCH ? · Issue #2388 · OpenMathLib/OpenBLAS

サンプルプログラムの作成

行列計算ベンチマーク

10000×10000 の行列乗算を 3 回実行し、性能を測定します。こちらも前回の Graviton での検証で使用したコードを流用します。

performance_test.cpp
#include <cblas.h>
#include <chrono>
#include <iostream>
#include <vector>
#include <unistd.h>

void run_benchmark(const char* label) {
    const int N = 10000;
    std::vector<double> A(N*N, 1.0);
    std::vector<double> B(N*N, 2.0);
    std::vector<double> C(N*N, 0.0);

    auto start = std::chrono::high_resolution_clock::now();

    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 duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);

    std::cout << label << ": " << duration.count() << " ms" << std::endl;
}

int main() {
    std::cout << "Available CPUs: " << sysconf(_SC_NPROCESSORS_ONLN) << std::endl;

    const char* num_threads = getenv("OPENBLAS_NUM_THREADS");
    std::cout << "OPENBLAS_NUM_THREADS: "
              << (num_threads ? num_threads : "not set") << std::endl;

    std::cout << "\nRunning 10000x10000 matrix multiplication...\n" << std::endl;

    run_benchmark("Test 1");
    run_benchmark("Test 2");
    run_benchmark("Test 3");

    return 0;
}

OpenBLAS ライブラリのバージョン指定

Amazon Linux 2023 では、OpenBLAS が複数のバージョンで提供されています。

リンカオプション ライブラリファイル 種類 説明
-lopenblas libopenblas.so Serial 版 シングルスレッド(1コアのみ使用)
-lopenblaso libopenblaso.so OpenMP 版 マルチスレッド(OpenMP 使用)
-lopenblasp libopenblasp.so pthread 版 マルチスレッド(pthread 使用)

今回は科学技術計算で広く使用される OpenMP 版(-lopenblaso)を使用します。

コンパイルと実行

シングルスレッド版とマルチスレッド版(OpenMP 版)の 2 つをコンパイルして性能を比較します。

# Serial 版のコンパイル
g++ -O3 performance_test.cpp -o test_serial -lopenblas

# OpenMP 版のコンパイル(マルチスレッド)
g++ -O3 performance_test.cpp -o test_parallel -lopenblaso -fopenmp

Serial 版の実行

export OPENBLAS_NUM_THREADS=1
./test_serial
Available CPUs: 4
OPENBLAS_NUM_THREADS: 1

Running 10000x10000 matrix multiplication...

Test 1: 36785 ms
Test 2: 36775 ms
Test 3: 36772 ms

OpenMP 版の実行(2コア使用)

マルチコアスケーリングを確認するため、2 コアで実行します。

export OPENBLAS_NUM_THREADS=2
./test_parallel
実行結果
Available CPUs: 4
OPENBLAS_NUM_THREADS: 2

Running 10000x10000 matrix multiplication...

Test 1: 18319 ms
Test 2: 18311 ms
Test 3: 18311 ms

OpenMP 版の実行(4コア使用)

c7a.xlarge のすべてのコアを使用して実行します。

export OPENBLAS_NUM_THREADS=4
./test_parallel
実行結果
Available CPUs: 4
OPENBLAS_NUM_THREADS: 4

Running 10000x10000 matrix multiplication...

Test 1: 9192 ms
Test 2: 9200 ms
Test 3: 9210 ms

AMD EPYC 最適化を試してみる

GCC のアーキテクチャ固有最適化オプション

GCC の -march-mtune で CPU アーキテクチャに応じた最適化を指定できます。通常は両方を同じ値に設定するようです。

AMD Zen アーキテクチャのターゲット

GCC での AMD Zen アーキテクチャサポート。

GCC バージョン サポートターゲット 対応アーキテクチャ
GCC 9 -march=znver2 Zen 2(EPYC 7002 "Rome")
GCC 11 -march=znver3 Zen 3(EPYC 7003 "Milan")
GCC 13 -march=znver4 Zen 4(EPYC 9004 "Genoa")

参考: GCC x86 Options - AMD x86 Options

Amazon Linux 2023 の GCC 11.5 では znver4 がサポートされていないため、以下の 2 パターンを試してみます。

  1. -march=native: 実行中の CPU を自動検出して最適化
  2. -march=znver3: Zen 3 向け最適化(Zen 4 でも動作)

参考: https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html

GCC 11.5 での最適化コンパイル

-march=native での最適化

実行中の CPU を自動検出して最適化します。

g++ -O3 -march=native -mtune=native performance_test.cpp -o test_native -lopenblaso -fopenmp

4 コアで実行します。

export OPENBLAS_NUM_THREADS=4
./test_native
実行結果
Available CPUs: 4
OPENBLAS_NUM_THREADS: 4

Running 10000x10000 matrix multiplication...

Test 1: 9185 ms
Test 2: 9203 ms
Test 3: 9193 ms

-march=znver3 での最適化

Zen 3 向けに明示的に最適化します。Zen 4 は Zen 3 の上位互換であるため、このオプションも有効なはずです。

g++ -O3 -march=znver3 -mtune=znver3 performance_test.cpp -o test_znver3 -lopenblaso -fopenmp

4 コアで実行します。

export OPENBLAS_NUM_THREADS=4
./test_znver3
実行結果
Available CPUs: 4
OPENBLAS_NUM_THREADS: 4

Running 10000x10000 matrix multiplication...

Test 1: 9304 ms
Test 2: 9309 ms
Test 3: 9301 ms

Clang 19 での Zen 4 最適化

Zen 4 に最適化してみたいので現時点で最新の Clang 19 を使ってコンパイルしてみます。

Clang 19 のインストール

sudo dnf install -y clang19

バージョンを確認します。

clang++-19 --version
実行結果
clang version 19.1.7 (AWS 19.1.7-13.amzn2023.0.1)
Target: x86_64-amazon-linux-gnu
Thread model: posix
InstalledDir: /usr/lib64/llvm19/bin
Configuration file: /etc/clang19/x86_64-amazon-linux-gnu-clang++.cfg

-march=znver4 での最適化コンパイル

Zen 4(EPYC 9R14)専用の最適化オプションを使ってコンパイルします。

clang++-19 -O3 -march=znver4 -mtune=znver4 performance_test.cpp -o test_znver4 -lopenblaso -fopenmp

4 コアで実行します。

export OPENBLAS_NUM_THREADS=4
./test_znver4
実行結果
Available CPUs: 4
OPENBLAS_NUM_THREADS: 4

Running 10000x10000 matrix multiplication...

Test 1: 9226 ms
Test 2: 9216 ms
Test 3: 9213 ms

測定結果のまとめ

実行結果一覧

設定 コンパイラ オプション コア数 実行時間(ms) 速度比
Serial 版 GCC 11.5 -O3 -lopenblas 1 36,777 1.00x
OpenMP 版 GCC 11.5 -O3 -lopenblaso -fopenmp 2 18,286 2.01x
OpenMP 版 GCC 11.5 -O3 -lopenblaso -fopenmp 4 9,240 3.98x
-march=native GCC 11.5 -O3 -march=native -mtune=native 4 9,166 4.01x
-march=znver3 GCC 11.5 -O3 -march=znver3 -mtune=znver3 4 9,168 4.01x
-march=znver4 Clang 19 -O3 -march=znver4 -mtune=znver4 4 9,212 3.99x

考察

マルチコアスケーリング

OpenBLAS はコア数に応じてほぼ理想的にスケールしました。ここは想定通りです。

  • 1 コア → 2 コア: 2.01 倍の性能向上
  • 1 コア → 4 コア: 3.98 倍の性能向上

理論値(2 倍、4 倍)にほぼ到達しており、OpenBLAS のマルチスレッド実装は効率的に動作しています。行列計算のような並列性の高いワークロードでは、コア数を増やすことで直線的に性能が向上することを確認できました。

コンパイラ最適化の効果は限定的

AMD 固有の最適化オプション(-march=native-march=znver3-march=znver4)による性能向上はほとんど見られませんでした

  • 標準の -O3: 9,240 ms
  • -march=native: 9,166 ms(0.8% 改善)
  • -march=znver3: 9,168 ms(0.8% 改善)
  • -march=znver4: 9,212 ms(0.3% 改善)

今回のプログラムでは計算時間の大部分が事前コンパイル済みの OpenBLAS ライブラリで消費されます。 アプリケーションコードのコンパイラ最適化は、計算以外のわずかな部分にしか影響しません。

そのため、アプリケーション側のコンパイラ最適化による性能向上は限定的です。自前の計算ロジックを多く含むプログラムでは、最適化の効果が期待できます。

GCC 11.5(-march=znver3)と Clang 19(-march=znver4)の性能差もほとんどありません。これも OpenBLAS が計算の主体であったためです。Clang 19 は Zen 4 用の -march=znver4 をサポートしていますが、今回の検証では GCC 11.5 の -march=znver3 と同等の性能でした。

まとめ

AMD EPYC Zen 4 でのビルド環境構築と行列計算の性能検証を通じて、以下を学びました。

ビルド環境について

AMD でもビルドするだけなら比較的容易な印象です。ただし最大限のパフォーマンスを引き出すには AMD 固有の最適化オプションの理解が必要です。自前の計算ロジックを多く含むプログラムでは、AMD 固有の最適化オプションがより大きな効果を発揮する可能性があります。

学んだこと

今回の検証を通じて、以下の理解が深まりました。

  • コンパイラ最適化オプション(-march-mtune)の役割
  • SIMD 拡張命令セット(AVX、AVX2、AVX-512)
  • 事前コンパイル済み外部ライブラリによる依存すること

おわりに

Intel ワークステーションで運用している行列計算プログラムを AWS の AMD EPYC 環境で動かすための検証をしました。

C++ のビルド環境について実際に手を動かすことで理解が深まりました。とくにコンパイラ最適化オプション(-march-mtune)の使い方や、SIMD 拡張命令セット(AVX、AVX2、AVX-512)の確認方法について学ぶことができました。

OpenBLAS を使った行列計算では、コンパイラ最適化の効果が限定的であることも分かりました。Graviton で試したときも同じだったのでわかってはいたのですが改めて試してみました。

参考

この記事をシェアする

FacebookHatena blogX

関連記事