tsune Help

A64Fx knowledge

アドカレ6日目

こんにちは, 18歳学生です. 自己紹介は他のブログを観察してください.

1人アドベントカレンダーも開催しています. Advent Calendar 2025

ここでは夏に理化学研究所で富岳を触りまくったので一般に知られている知見についてまとめようと思います.

preface

A64Fxとはスパコン富岳に積まれているFujitsu製のCPUである.

スパコンでは珍しくaarch64が採用されている. 昨今googleやamazonのデータセンターでarmプロセッサーへの移行が進んでいるが一足先にarmが採用されたプロセッサである.

aarch64のスパコンそのものが珍しいはずだった... NvidiaのDGX SuperPodにはArmの独自cpuが乗っているため, 世の中のスパコンがどんどんarmに移行してきているのかもしれない.

個人的にarmのアセンブリはあまりにも可読性にかけるため,x86_64の存命に期待している.

docs

A64Fxのありがたい点としてプロセッサの情報がghにpdfで上がっている.

https://github.com/fujitsu/A64FX/tree/master/doc

https://www.fujitsu.com/downloads/JP/jsuper/a64fx/a64fx_datasheet_jp.pdf

大事そうな情報だけまとめよう

  • SVE, SVE2に対応

  • SIMD幅は512bit

  • movprfx命令のデコードが特殊

  • freqは1.8GHz,2.0GHz, 2.2GHz,2.6GHz固定

ここで人類が驚くべきはSIM幅が512bitであることだ. どこかの中途半端なintelのAVX512とは違い, というかSVEのsimd幅は可変なので256も128もアリ得るが素晴らしいレジスタサイズである.

残念ながらSMEには非対応, 富岳Nextに積まれる予定のCPU, FUJITSU- MONOKA-XでSMEに対応するとのことで非常に期待している. https://www.riken.jp/pr/news/2025/20250822_1/index.html

また,CPU周波数が固定であることは研究的にすごくありがたいことで関数のレイテンシにFreqをかけることで関数1回あたりにかかるCPUクロック数を測定出来たりする.

SVE 101

armのintrinsicsガイドが非常に強力: https://developer.arm.com/architectures/instruction-sets/intrinsics/

まず, 前述の通りSVEのsimd幅は可変なのでvlenを使ってSIMD幅を獲得する. https://developer.arm.com/architectures/instruction-sets/intrinsics/#q=vlen

あと重要な要素としてpredicate registerが存在する. 皆様,特に本校の学生さんのマシンは大体x64でAVXを組む人がいると思うがこのような概念はないので若干戸惑うと思う.

macを使っている人もいるだろうがmacを使っている人なんて意識が高くて低レイヤののアセンブリを見たことがないのでpredicate registerについて知見がないと思う.

  • example: ある関数funcを呼び出し,返り値を加算し続けるforループ

const std::size_t vlen = svcntd(); svfloat64_t sum = svdup_f64(0.0); for (std::size_t i = 0; i < SIZE; i += vlen) { svbool_t active = svwhilelt_b64(i, SIZE); svfloat64_t x = svld1_f64(active, &data_ptr[i]); sum=svadd_f64_x(active,sum, func(x,active)); } return sum;
  • fyi: avx version

__m256d sum = _mm256_setzero_pd(); for (std::size_t i = 0; i < SIZE; i += 4) { __m256d x = _mm256_load_pd(&data_ptr[i]); sum = _mm256_add_pd(sum, func(x)); } return sum;

predicate registerをマスターすればあとはAVXと同じ感じで計算ができる.

他に注意することがあるとすればSVEのintrinsicsは引数に符号を渡せないのでsub(a,b)add(a,-b)でサボることが出来なかったり, FMA( )の計算では以下のようなintrinsicsを使い分ける必要がある.

svmls[_f64]_m op1[i] - op2[i] * op3[i] svmla[_f64]_m op1[i] + op2[i] * op3[i] svnmla[_f64]_m -(op1[i] + op2[i] * op3[i]) svnmls[_f64]_m -(op1[i] - op2[i] * op3[i])

FMA( )自体はCPUのパイプライン並列性を考えるときに採用できるならしたほうがいいので,画面前のそこのあなたも画面後ろのそこのあなたもこの苦行を味わう日も近いのかもしれない.

tips

movを減らす

例えば,x86_64で考えてみよう.

puts("hello world");

これをgccでノーオプションでコンパイルすると以下のようなアセンブリになるはずだ.

mov rax,[rip+0x114514] #<-ptr of "hello world" in .rodata mov rdi, rax call puts@plt

gccのO3とかでコンパイルするとrdiに直接ロードされるようになると思う.

mov rdi,[rip+0x114514] #<-ptr of "hello world" in .rodata call puts@plt

movという命令は前後命令への依存度が高いのでパイプラインへの悪影響を与える.

これをSVEで考えるとld1rd命令が該当する. ld1rd命令は低数値を全てのレーンにロードする命令で,ループで定数値をかけていくときに素晴らしいレイテンシを達成できる. https://developer.arm.com/documentation/111182/2025-09_ASL1/SVE-Instructions/LD1RD--Load-and-broadcast-doubleword-to-vector-

富岳のコンパイラFCCで普通にコンパイルするとスカラーレジスタに値を書き込んでからベクトルレジスタに値を移すような処理を見るかもしれないがそれをld1rd命令によって解消できる. p117. A64FX_Microarchitecture_Manual_en_1.0.pdf以降にはSVE命令のA64FXでのレイテンシが書かれているので参考にすると良いだろう

ld1rdについて仕様を確認するとld1rdはEAG,単一パイプラインのみを使用する. A64Fxはパイプラインが2つあるのでld1rdを利用することでさらなるパイプライン並列性を確保することができる.

  • ld1rd

    • 11

    • EAG

A64Fxではmovprfx命令が最適化されている(p14,p32. A64FX_Microarchitecture_Manual_en_1.0.pdf)

ref

Last modified: 05 December 2025