2.10 N 体計算ベンチマーク
理化学研究所

計算科学研究機構
似鳥 啓吾

2015/3/17 の第 5 回と 2015/7/1 の第 6 回の WG とで報告を行った。PowerPoint の資料
は発表時のものをそのまま掲載する。立て続けに発表することになった経緯は、第 5 回の終わりに次回発表者を決める際に、似鳥がうっかりと「次回までに FX100 を触っていることになっている人にしよう」などと発言したためである。発表したのは Hermite 積分法による N 体計算ベンチマークコード(github.com/nitadori/Hermite)とその結果である。最初は AVX と HPC-ACE の間で SIMD マルチコアな CPU の単体性能を比較しようというぐらいのつもりで書き始めたコードだったのだが、その後の拡張により最近では
Xeon-Phi KNC、CUDA (Kepler)、AVX2、HPC-ACE2 にも対応している。GPU 対応について
は、メモリ空間が分断されていることと倍精度の高速なものは高価だったことから当初は乗り気ではなかったのだが、2014 年秋の科学技術計算分科会でのパネルディスカッションで MIC との比較を行うことになった機会に実装したものである。
第 5 回の WG ではコードの概要説明と HPC-ACE 向け実装について報告した。発表資料には一点間違いがあり、ベンチマークで使用した FX10 は 1.85 GHz のものではなく実は1.65 GHz であった。対ピーク性能比に対して 1 割強の過小評価をしていたことになる。
HPC-ACE 用の実装で苦労した点は、C++での記述から十分に最適化されたコードを得ることである。筆者は 2012 年秋の科学技術計算分科会のパネルで、簡単なクラスや構造体に対してなかなか思ったようなコードが得られない（変数がレジスタに乗らずにメモ
リアクセスになる）とクレームを付けていたのだが、この頃と比べると Fujitsu コンパ
イラの C++最適化は劇的に改善されており、ある程度気をつけて（ほぼ自明なコピーコ
ンストラクタや代入演算子も忘れずに定義することなど）書けば変数が全てレジスタに乗るような理想的なコードが出力されることが確認できた。実際のベンチマーク結果であるが、粒子数が少ない領域での性能低下が若干気にはなるもののおおむねピーク性能の 2/3 程度は出ており、積和算ではない演算もそれなりに含まれていることを考えれば十分な最適化がなされたコードといえる。富士通製品のチューニングの話題からは離れるが、Xeon Phi の小粒子数領域での性能低下は相当に深刻なものである。筆者のチューニングが不十分という可能性もあるのかもしれないが（少なくとも手を抜いたつもりはないのだが）、ホスト CPU からの明示的なデータコピーとカーネル呼び出しを含む
CUDA 版の実装以上に小粒子数での同期オーバーヘッドが発生していることがわかる。
第 6 回の WG では、
新規にアクセス可能になった理研和光の FX100 及び 16 コアの Xeon

179 / 253

E5 v3 (Haswell)でのベンチマーク結果を報告した。前回からのコード改善点として、OpenMP による並列化の部分で必要以上の同期やパラレルスコープの開閉が含まれていた部分が修正されている。この変更でどのアーキテクチャでも粒子数の小さな領域での効率が改善された。FX100 での新命令セット HPC-ACE2 に対して本ベンチマークコードを移植するにあたって最大の問題だったのは、当時のコンパイラでは SIMD 組込み関数
が未サポートであったことである。このことはマニュアルにも明示的に書かれていたが、「一切サポートしない」というよりはもう暫くの間待つようにという話のようであった。
開発陣が気を利かせてくれたのかどうかは定かではないが、処理系のインクルードパスを見てみるとそれらしいヘッダファイルは存在したので、これを使ってみることにした。
未サポートの機能を勝手に使おうというのだからこれは完全に自己責任（とはいえシス
テムを破壊するようなものではない）なのだが、少し間違えるとコンパイラのプロセスを落としてしまったり（コアダンプが出るだけでシステム障害は引き起こさない）といった試行錯誤があった。注意が必要だったのは、HPC-ACE 互換の動作をする SIMD2 モード、HPC-ACE2 で拡張された動作をする SIMD4 モードの間は切り替え命令で行き来するという点である。インテルアーキテクチャでは命令長をどんどん延ばすかたちで命令フ
ィールドの拡張が可能であるが、固定長命令の SPARC ではそのようなことはできないのでこのような仕組みになっている。当時のコンパイラではこのモード切替をソースコードの文脈から認識して行うという部分に問題があったようで、逆にこの部分を手動で行うことで（未サポートの）SIMD 組込み関数は十分に実用的であった。性能であるが、同じ 16 コアで比べた時に FX100 が Xeon より若干良い数字になっている。Xeon は定格
で 2.3 GHz、AVX 使用時の TurboBoost が 2.5 GHz であり、FX100 は 1.975 GHz で演算器の数は 256-bit dual FMA と共通している。やはりレジスタの本数が 128 本と多いことが効率向上に寄与しているものと思われる。ただし粒子数の少ない領域では若干見劣りする結果となっており、同期やコア間通信が高速なアーキテクチャなだけにこの部分をもう少し頑張って欲しいと思う。Xeon-Phi であるが、粒子数が十分に大きな領域で Xeonの 1.5 倍程度の性能となっている。Xeon に AVX-512 が実装されてしまうと、演算器の数、レジスタ幅、レジスタの本数が全て２倍となるため、この商品の系列がどうなってしまうのか不安になる。

180 / 253

N体計算ベンチマーク
2015/3/17 SS研P‐PAP WG
理研AICS 似鳥啓吾

話の予定
1. 計算コードの概要
2. HPC‐ACE向けの実装
3. 性能

181 / 253

コードの概要• ブロック時間刻みHermite積分法のN体計算ベンチマーク（詳細は後述）– 球状星団や惑星系形成で広く使われているスキーム• 複数アーキテクチャに向けて手動SIMD化– これまでにAVX, HPC‐ACE, Xeon‐Phi, CUDAのバージョン– フル倍精度演算をベンチマークのルールとした– 現状シングルノードのみ対応• 4, 6, 8次精度の積分法を実装– 重力の高階導関数を解析的に計算– アーキテクチャごとに3種類のカーネルチューンが発生• 言語は「一応」C++03– 移植性、可読性、可搬性は考慮されていない– ソースは github.com/nitadori/Hermite/SRC/

N体計算の支配方程式• 本当にこれを積分するだけ– 細かな話は無数にあるわけですが• 減算３回、積和６回、乗算３回、逆数平方根１回• i‐loopとj‐loopの二重ループ、O(N2)• j‐粒子がi‐粒子を引っ張る• i‐並列とj‐並列のふたつの並列度– i‐並列ではj‐粒子の放送によってメモリ帯域を節約– j‐並列では最後に部分力の総和演算が必要
182 / 253

ループボディ••

流用資料につき混合精度
外側ループのomp parallel
化は自明に近い•レジスタが余っていたら外側ループをアンロール
j‐粒子がキャッシュに収まるようなブロック化／並列化も有効•

Hermite積分法
f
i

i+1

v
t

• 加速度の時間微分を直接計算• ２点間のでの補間多項式から４次精度を得る• 予測子修正子法のひとつ• もっと高階の微分を用いて６次、８次精度も可能
183 / 253

力の高階微分• 式を整理すると項数は線形にしか増えない– 次数の二乗のコスト• 積和算の比率が増える• レジスタ消費も増える• 平方根は一回だけ

独立時間刻み法 (Aarseth 1963)• 粒子ごとの時間刻みΔtiを持つことを許す– 他の粒子の座標に「予測子」を用いることで実現– 最近では並列度を稼ぐために刻みを二冪に丸める• 同時に積分できる粒子数はそんなに多くない– Nact∝N2/3 程度でGPUのスレッド数より少ない– 一回の計算量がO(NactN)
1/1

1/2

1/4

1/8

184 / 253

1ステップの手続き
1.
2.
3.
4.
5.

積分する粒子を選ぶ：O(Nact) or O(logN) (serial)
全粒子の予測子を計算：O(N) (parallel)
重力を計算：O(NactN) (parallel)
修正子と新しい刻み幅を計算：O(Nact) (parallel)
粒子を時間刻でソート：O(NactlogNact) (serial)• GPUでは2と3をオフロード、ホストとの通信はO(Nact)• Nact∝N2/3, 系にもよるが係数も１ぐらい– 細かな同期の入るコードになる– ステップあたりO(N1.66)の相互作用計算、単位時間の積分にO(N0.66)ステップ、トータルO(N2.33)

HPC‐ACE向けのチューニング
• コンパイラベースのSIMD化には制限が多い– 粒子法全般、他社製品も同様– 分岐の排除が職人芸的だったり、外側ループの並列度が有効だったりするため• 幸い、Kernelだけ組込み関数で書くのはそれ程大変ではない– 比較的単純なホットスポットが明確なため• 方針– 組込み関数クラスラッパでできるだけスッキリと書く– 明示的なSIMD化と同時に、ロード命令を極力減らす（レジスタレベルの再利用）

185 / 253

SIMD組込み型／関数• C/C++言語使用手引書の巻末に一覧がある– v2r8型 (vector 2 real‐kind 8)、v2r4型、v2i8型– __builtin_fj_xxx()のような組込関数多数– 倍精度演算はSSE2/3と同じような使い勝手

積和命令• 符号も入れて6‐bit分のフィールド、64個の組込み関数– madd : D := A×B + C– msub : D := A×B − C– nmadd : D := −(A×B + C)
– nmsub : D := −(A×B − C)
– _sr1 : Aの上下語swap– _sr2 : Bの上下語swap– _cp : Aの下位語放送（sr1と合わせて上位語放送）– _neg : A×Bの上位語符号反転– 最長でnmsub_cp_neg_sr12_v2r8()• DGEMMや複素数のことを考えた設計• オペランド放送は粒子法にも有用

186 / 253

クラスラッパ
static inline v2r8 gp3m_force(
const v2r8 r2, const v2r8 r, const v2r8 rinv){
const v2r8 mask = v2r8_cmplt(r2, immr8(4.0));
const v2r8 s= v2r8_sub(v2r8_max(r, immr8(1.0)), immr8(1.0));
const v2r8 s2 = v2r8_mul(s, s);const v2r8 s6 = v2r8_mul(s2, v2r8_mul(s2, s2));
const v2r8 poly1 =v2r8_madd(
v2r8_msub(
v2r8_madd(
v2r8_msub(
v2r8_msub(
immr8(3./20.),r,
immr8(12./35.)),r,
immr8(1./2.)),r,
immr8(8./5.)),r2,
immr8(8./5.)),r2,rinv);
const v2r8 poly2 =v2r8_nmsub(
v2r8_madd(
immr8(3./35.),
rinv,
v2r8_madd(
immr8(1./5.),r,
immr8(18./35.))),
s6,
poly1);
return v2r8_and(poly2, mask);}• 流石にこんなものを生で書き続けるのはしんどい• 多項式ぐらいは演算子で書きたい• C++ならクラスに演算子定義できる– まともにレジスタに載るコードが生成できたのは２年前ぐ
らい←2012年春のコード

クラスラッパ（続き）
組み込み型をラップする

演算子は普通に定義

struct v2r8{
typedef __builtin_v2r8 _v2r8;_v2r8 val;

v2r8 operator+(const v2r8 rhs) const {return v2r8(__builtin_fj_add_v2r8(val, rhs.val));}v2r8 operator‐(const v2r8 rhs) const {return v2r8(__builtin_fj_sub_v2r8(val, rhs.val));}v2r8 operator*(const v2r8 rhs) const {return v2r8(__builtin_fj_mul_v2r8(val, rhs.val));}v2r8(const v2r8 &rhs) : val(rhs.val) {}v2r8 operator=(const v2r8 rhs){
val = rhs.val;
return (*this);}// コピーコンストラクタと代入演算子は忘れないこと
v2r8() : val(__builtin_fj_setzero_v2r8()) {}v2r8(const double x): val(__builtin_fj_set_v2r8(x, x)) {}v2r8(const double lo, const double hi): val(__builtin_fj_set_v2r8(lo, hi)) {}v2r8(const _v2r8 _val) : val(_val) {}operator _v2r8() { return val; }•積和化はコンパイラがやってくれる•

参照渡しより値渡しの方が上手くレジスタに載ってくれることが多い};

187 / 253

自動アンロール型、放送型• レジスタを２本消費しレイテ
ンシを隠蔽• AVXやHPC‐ACE2と互換
struct v4r8{
typedef __builtin_v2r8 _v2r8;_v2r8 v0, v1;• 上下語を放送して演算• 積和命令を騙して使う
struct v2r8_bcl{
typedef __builtin_v2r8 _v2r8;_v2r8 val;
v2r8 operator‐(const v2r8 rhs) const {const v2r8 one(1.0);
return v2r8(__builtin_fj_msub_cp_v2r8(val, one.val, rhs.val));}v2r8 operator*(const v2r8 rhs) const {const v2r8 zero(0.0);
return v2r8(__builtin_fj_madd_cp_v2r8(val, rhs.val, zero.val));}v4r8(const v4r8 &rhs): v0(rhs.v0), v1(rhs.v1) {}v4r8 operator=(const v4r8 rhs){
v0 = rhs.v0;
v1 = rhs.v1;
return (*this);
}
};

};

粒子法での応用• ２粒子×２粒子で４相互作用並列
const v2r8 xi, xj;
const v4r8 dx = v4r8_llhh(xj) ‐ v4r8(xi);• 実際には、
msub_cp_v2r8(lhs, 1.0, rhs);
msub_cp_sr1_v2r8(lhs, 1.0, rhs);

が呼ばれる• 過度の抽象化は可読性を妨げるという典型
例

188 / 253

#pragma omp for // calculate partial force
for(int j=0; j<nj; j+=2){
const v2r8 mj = pred[j/2].mass;const v2r8 xj = pred[j/2].pos[0];const v2r8 yj = pred[j/2].pos[1];const v2r8 zj = pred[j/2].pos[2];const v2r8 vxj = pred[j/2].vel[0];const v2r8 vyj = pred[j/2].vel[1];const v2r8 vzj = pred[j/2].vel[2];最内ループ• 見た目だけは単純• コンパイラはここから更にアンロールとソフトウェア
パイプライニングする

const v4r8 dx = v4r8_llhh(xj) ‐ v4r8(xi);const v4r8 dy = v4r8_llhh(yj) ‐ v4r8(yi);const v4r8 dz = v4r8_llhh(zj) ‐ v4r8(zi);const v4r8 dvx = v4r8_llhh(vxj) ‐ v4r8(vxi);
const v4r8 dvy = v4r8_llhh(vyj) ‐ v4r8(vyi);
const v4r8 dvz = v4r8_llhh(vzj) ‐ v4r8(vzi);• プロファイラは対ピーク75%を報告const v4r8 r2 = ((v4r8(eps2) +dx*dx) + dy*dy) + dz*dz;
const v4r8 rv = (dx *dvx + dy *dvy) + dz *dvz;
const v4r8 rinv
= r2.rsqrta_x8();
const v4r8 rinv2 = rinv * rinv;
const v4r8 mrinv = rinv * v4r8_llhh(mj);const v4r8 mrinv3 = mrinv * rinv2;• 減算が積和命令で水増しされたため• 若干の$L1D待ちないし演算待ちが残るconst v4r8 alpha = v4r8(‐3.0) * rv * rinv2;
ax += mrinv3 * dx;
ay += mrinv3 * dy;
az += mrinv3 * dz;
jx += mrinv3 * (dvx + alpha * dx);
jy += mrinv3 * (dvy + alpha * dy);jz += mrinv3 * (dvz + alpha * dz);
} // for (j)

逆数平方根
•
•
•

8‐bit精度の近似命令がある
任意の次数の収束公式を構築可能
いくつか試したけどコンパイラの出すのと同じ二次収束を３回に落ち着いた

v2r8 rsqrta_x8() const {const v2r8 x(val);const v2r8 y0 =v2r8(__builtin_fj_rsqrta_v2r8(val));const v2r8 half(0.5);
const v2r8 xhalf = x * half;const v2r8 h0half = half ‐ y0*(y0*xhalf);
const v2r8 y1 = y0 + y0*h0half;// x2
const v2r8 h1half = half ‐ y1*(y1*xhalf);
const v2r8 y2 = y1 + y1*h1half;// x4
const v2r8 h2half = half ‐ y2*(y2*xhalf);
const v2r8 y3 = y2 + y2*h2half;// x8
return y3; // 4‐mul, 6‐fma}••

SSE/AVXだと12‐bit相当
こちらは五次収束一発を採用#define REP4(x) {x, x, x, x}
static inline v4df v4df_rsqrt(const v4df x){
const v4df y0 =__builtin_ia32_cvtps2pd256(__builtin_ia32_rsqrtps(__builtin_ia32_cvtpd2ps256(x)));const v4df c0 = REP4(1.0);
const v4df c1 = REP4(0.5);
const v4df c2 = REP4(3./8.);
const v4df c3 = REP4(5./16.);
const v4df c4 = REP4(35./128.);
const v4df h = c0 ‐ x*y0*y0;
const v4df y1 = y0 * (c0 + h * (c1 +h * (c2 + h * (c3 + h * (c4)))));
return y1;}

189 / 253

Kernel profile (thanks Dr. Maruyama)• 上から順に４次、６次、８次のカーネル

Haswell i7 4C8T 3.40 GHz,
積分も含めた実行性能 • Intel
16 flops/cycle, 207.6 Gflops• Fujitsu FX10 16C16T 1.848 GHz,
8 flop/cycle, 236.5 Gflops

190 / 253

性能の解釈• Dual FMAのCPUで一応ピークの半分程度– 対ピークではFX10の方がいい、これはレジスタが16
本か128本かが効いていそう• 低粒子数でのFX10の起ち上がりが若干悪い– 最内ループをスレッド並列にしたので短くなりすぎた– もう少し真面目に割り当てれば改善するかも（同期は速いはず）– 高次積分法の方が起ち上がりがよく、並列計算機に向いている

おまけ（MICとGPU）

191 / 253

• Intel KNC 5110p 60C240T 1.053 GHz,
16 flop/cycle, 1011 Gflops• GeForce Titan 14SMX, 64 FMA/SMX,
1300 Gflops

拡大図• ホストとの通信の無いMICが低粒子数で豪快に遅い• OpenMPの同期がCUDAのAPIコールより数倍遅いため

今後• Xeon E5 v3 (12C, 16C)を調達したので測ってみたい– AVX‐512は当面先になりそう• 和光のHOKUSAIの試験利用申請に名前を入れてもらった– マニュアルとコンパイラにアクセスできるようになる見
通し• MPI並列版も要望がきたのでやってみる– HPLのようにコミュニケータを2次元分割– 集合通信のレイテンシに敏感なコードになる

192 / 253

N体計算ベンチマーク（FX100 Hokusaiを使ってみた）
2015/7/1 SS件P-PAP WG
理研AICS 似鳥啓吾

コードの概要と宿題• 色々なSIMDアーキテクチャ向けにN体計算
ベンチマークコードを書いている– AVX, HPC-ACE, MIC KNC, AVX2, CUDA
– C++とSIMD組込み関数、OpenMPを使用• 宿題– 理研和光のFX100アカウントが発生したので何か試す– 16コアのHaswell Xeonを調達したのでベンチマーク
2
193 / 253

前回からのアップデート• OpenMPの同期オーバーヘッドを軽減した– Core i7 4C/8TからXeon 16C/32Tに行くとparallel
スコープの開閉コストが馬鹿にならなくなってきたため– parallelスコープは開きっぱなしにして中でbarrierを入れながら並列化した• AICS研究部門保有のFX10は1.83GHzではなく1.65GHzだった– ピーク性能の計算間違い、結果対ピークは改善• FX100のHPC-ACE2版のコードを追加
3

HPC-ACE2版の障壁

4
194 / 253

「SIMD組込み関数は未サポート」• 丁寧にヘッダでコンパイルエラーにしている<emmintrin.h># if defined(__HPC_ACE2__)#
error "HPC‐ACE2 instruction set not enabled"# endif /* defined(__HPC_ACE2__) */• ただしバイナリ互換はあるので京やFX10でコンパイルしたものを持ってくるという手はある

5

別のヘッダを漁ってみる$ ls /opt/FJSVmxlang/GM‐2.0.0‐02/include/blas_enum.hfjcoll.hblas_extended_proto.h fjcoll_lib.hc++fjcoll_lib.modc++521fjhook.hc++fastfjintrin.hcomplex.hfjmfunc.h
cssl.hfloat.hemmintrin.h
ieee_arithmetic.mod
fast_dd.h
ieee_exceptions.mod
fast_dd.mod
ieee_features.mod
fast_dd_inline.h
iso646.hfeatures.h
iso_c_binding.mod
fj_lapack.h
iso_fortran_env.mod
fj_tool
lapacke.h
fjcex.hlimits.hmath.hmpi
mxintrin.h
omp.h
omp_lib.h
omp_lib.mod
omp_lib_kinds.mod
service_routines.mod
setjmp.hstdarg.hstdbool.h
stddef.htgmath.hvampirtrace
varargs.h
6

195 / 253

mxintrin.h• HPC-ACE2の命令が網羅されているっぽい• ぱっと見「使えそう」、現状では制限も多い– SIMD長が4か2かのモード切り替えはユーザーの責任で– 一部の関数は呼ぶだけでコンパイラがsegmentation
fault– 一部の関数は呼び方によってはコンパイラがsegmentation fault– 最適化レベルの指定が低いとコンパイラがsegmentation fault• 未サポートの機能を勝手に使おうという方が悪い

7

SIMD mode 2/4• モード切り替え命令でSIMD2/SIMD4を切り替える– SIMD2: K/FX10互換の動作 (SSE相当)– SIMD4: FX100での拡張 (AVX相当)– 命令フィールドに空きがなかった？
• 切り替えコストはnop程度の模様• sxar同様命令フィールドを補う命令らしい– 切り替え方を間違えるとillegal instructionで落ちたり結果が半分だけになったり• 基本コンパイラが発行してくれる命令です8
196 / 253

OpenMPとの組み合わせ• 若干コンパイラの挙動に問題が残っているよ
うなので要注意– くどいけど悪いのは未サポートの機能を勝手に使っている側です#pragma omp parallel{_fjsp_simd_mode_4(); // optimized out of the scopeasm volatile (“ssm 1”); // WTF, it runs!!
v4r8...
}
9

Element Concatenate Shift Left
• 要素を２つ連結して左にシフト– 64-bit単位で即値シフトできるので色々なシャッフルに応用可能ecsl a, b, 1

a0

a1

a2

a3

b0

b1

b2

b3

ecsl a, b, 2
ecsl a, b, 3

10
197 / 253

応用: 4×4行列転置• 一度作っておくと重宝する• メモリ経由でstride loadでも可能
static void transpose(v4r8 &a, v4r8 &b, v4r8 &c, v4r8 &d){// 6‐fsel, 8‐ecsl, 2‐mask‐reg
const v4_mask m1010(1,0,1,0);
const v4_mask m1100(1,1,0,0);v4r8 a0b0a2b2 = m1010.select(a,b.ecsl(b,3));//a0a1a2a3
b3b0b1b2
v4r8 a1b1a3b3 = m1010.select(a.ecsl(a,1), b);//
a1a2a3a0
b0b1b2b3
v4r8 c2d2c0d0 = m1010.select(c.ecsl(c,2), d.ecsl(d,1));//
c2c3c0c1
d1d2d3d0
v4r8 c3d3c1d1 = m1010.select(c.ecsl(c,3), d.ecsl(d,2));//c3c0c1c2
d2d3d0d1
v4r8 a0b0c0d0 = m1100.select(a0b0a2b2, c2d2c0d0);
v4r8 a1b1c1d1 = m1100.select(a1b1a3b3, c3d3c1d1);
v4r8 a2b2c2d2 = a0b0a2b2.ecsl(c2d2c0d0, 2);
v4r8 a3b3c3d3 = a1b1a3b3.ecsl(c3d3c1d1, 2);a = a0b0c0d0; b = a1b1c1d1; c = a2b2c2d2; d = a3b3c3d3;
}

11

実装方針
• 組込み型、組込み関数はとりあえずC++のクラスでラップ– この部分はコンパイラが期待通りにインライン展開してくれた• 粒子間相互作用のSIMD化には放送ロード命令を多用した– レジスタ内でのシャッフルは控えた（積和とポート競合しそうだったから）– 自動アンロール型のような複雑な機能は今回は控えた（とりあえず動かすことを優先）
12
198 / 253

OpenMPチューニング
• 前回まで：いくつかのループに#pragma
omp parallel forをつけていた– 簡単だが開始／終了オーバーヘッドが大きい• 今回のチューン：パラレルスコープは長期間開いておき、omp for nowaitやomp
barrierで書く– コードの書き直し量が多い– 少し同期を間違えるとデータ化け13

性能評価• Xeon E5-2698 v3: 16C32T,
640Gflops@2.5GHz (AVX turbo)• Xeon Phi 5110P: 60C240T, 1.053GHz,
1011Gflops• FX10: 16C, 1.65GHz, 211Gflops• FX100: 32C, 1.975GHz, 1011Gflops– 16スレッドと32スレッドで測定

14
199 / 253

Performance (Xeon)
Gflops Xeon 16C/32T (peak 640)
350
300
250
200
150
100
50
0
1000

4th
6th
8th
10000

100000N
15

Performance (Xeon Phi)
Gflops KNC 60C/240T (peak 1010)
500
450
400
350
300
250
200
150
100
4th
6th
8th

50
0
1000

10000

100000N
16200 / 253

μsec/block-stepsec/block Xeon vx. MIC
100000

10000

1000

MIC 4th
MIC 6th
MIC 8thXeon 4th
Xeon 6th
Xeon 8th
 x5/3

100

10
1000

10000

100000N
17

FX10 (1.65GHz)
Gflops FX10 16C (peak 211)
160
140
120
100
80
60
40
4th
6th
8th

20
0
1000

10000

100000N
18
201 / 253

FX100 (16T/32T)
Gflops FX100 16T vs. 32T (peak 505 vs. 1011)
700
600
500
400
300
200

16T 4th
16T 6th
16T 8th
32T 4th
32T 6th
32T 8th

100
0
1000

10000

100000N
19FX100 vs. Xeon
Gflops FX100 16T vs. Xeon (peak 505 vs. 640)
350
300
250
200
150
100

FX100 4th
FX100 6th
FX100 8th
Xeon 4th
Xeon 6th
Xeon 8th

50
0
1000

10000

100000N
20
202 / 253

まとめ
• 同じ256-bit dual FMA 16 coreの条件でFX100がHaswellより少し速かった– レジスタが128本と豊富にあるのが効いているのだと思う– PAを見るとまだ演算待ちが残っている• 粒子数の少ない所での性能起ち上がりではXeonより若干悪くなってしまった– 同期そのものは圧倒的に速いはず– ループ回転数が小さくなった為かも（外側ループアン
ロール等のチューニングの余地あり）
21

203 / 253