はじめに
前回までで4倍展開したループのSIMD化が完了した。いよいよ実測である。
コードは
https://github.com/kaityo256/lj_simdstep
においてある。
SIMD化完了
4倍展開したSIMD化が完了した。基本的に4倍展開されたループのSIMD化はほぼ自明だが、少しだけ非自明なのは、4倍展開して得られた4つの相対距離を一つのYMMレジスタにまとめ、力の計算を4ペア同時に行ってから、またバラす作業。まずまとめるところ。
v4df vr2_13 = _mm256_unpacklo_pd(vr2_1, vr2_3);
v4df vr2_24 = _mm256_unpacklo_pd(vr2_2, vr2_4);
v4df vr2 = _mm256_shuffle_pd(vr2_13, vr2_24, 12);
v4df vr6 = vr2 * vr2 * vr2;
v4df vdf = (vc24 * vr6 - vc48) / (vr6 * vr6 * vr2);
v4df mask = vcl2 - vr2;
vdf = _mm256_blendv_pd(vdf, vzero, mask);
vr2_1
からvr2_4
までには、(r2_1, r2_1, r2_1, 0)みたいなデータが入っている。ここから(r2_1, r2_2, r2_3, r2_4)を作るのに、unpack二回とshuffleを使っている。その後は普通に計算している。ただし相互作用範囲外のペアの処理が必要となるため、blendv_pdでマスク処理をしている。
次にバラすところ。
v4df vdf_1 = _mm256_permute4x64_pd(vdf, 0);
v4df vdf_2 = _mm256_permute4x64_pd(vdf, 85);
v4df vdf_3 = _mm256_permute4x64_pd(vdf, 170);
v4df vdf_4 = _mm256_permute4x64_pd(vdf, 255);
計算された力(df1,df2,df3,df4)から、(df1,df1,df1,df1)〜(df4,df4,df4,df4)を作っているこうすると力積ベクトルの計算がスムーズなので。非自明なところはこれくらいだと思う。
結果
計算手法 | 実行時間[sec] | 備考 |
---|---|---|
pair | 8.176446 | ペアごとにループをまわしたもの |
pair_swp | 6.995374 | 上記をソフトウェアパイプライニングしたもの |
pair_swp_intrin | 4.276301 | 上記を4倍展開してSIMD化したもの |
sorted_intrin | 3.882854 | i粒子でソートしたものをSIMD化したもの |
・・・ソートバージョンのSIMD化に負けた ○| ̄|_
それなりに加速したものの、ソートバージョンに低密度でも負けたので、ループ長が短いところで「ゴミがほとんど生じない」メリットは、メモリアクセスが増えるデメリットに負けたようだ。
調べてみると、ソートしてソフトウェアパイプライニングしたのの(SIMD化前)のIPCが2.12なのに対し、ソートしないでソフトウェアパイプライニングしたもの(SIMD化前)のIPCが1.99と若干低く、かつ命令数が増えてしまっているのが敗因のようだ。
残念ながら、今回の方針では早くならなかった。参考にならないだろうが、コードは
においておく。