この記事は前の記事の続きのつもりです。
「ロード」とか「ストア」とかの言葉は知っている人向け。
はじめに
SVEを用いて配列の計算を処理しようとすると(別にSVEに限らずAVXとかでもそうですが…)、
- 通常の配列(
double
など)をSVE型にload - SVE型どうしの演算
- SVE型を通常の配列にstore
という処理をする事になります。
ので、loadとstore命令を書かなくてはならないわけです。
ここにはSVEでのload/store命令のうち、代表的な物を書いておきます。
以下のサンプルコードでは、基本的に倍精度浮動小数点src
を一回SVEレジスタにloadして、dst
にstoreする、すなわちコピーを行う事を想定しておきます。
倍精度ではない値をやり取りしたいとかそういう場合は64を32に頭の中で入れ替えたりしてください。
前置きとして、プレディケータとして、
const svbool_t pg = svwhilelt_b64(i, N);
という物が定義されているものとします。
また、SIMD幅の中に存在する倍精度浮動小数点の数をN
としておきます。
スカラー値 load/store
srcポインタからSIMD幅分のデータを読み、SVE型に格納する。
const svfloat64_t data = svld1(pg, &src[i]);
svst1(pg, &dst[i], data);
svld1
でsrc[i]
からsrc[i + N - 1]
までの要素をdata
に格納し、
svld1
でその値をdst[i]
からdst[i + N - 1]
に書き戻している。
スカラー値 load/store、間隔vnum
srcポインタからSIMD幅分のデータを間隔vnum
個開けて読み、SVE型に格納する。
const uint64_t vnum = 0;
const svfloat64_t data = svld1_vnum(pg, &src[i], vnum);
svst1_vnum(pg, &dst[i], data, vnum);
svld1_vnum
でsrc[i + N*vnum]
からsrc[i + N*vnum + N - 1]
までの要素をdata
に格納し、
svst1_vnum
でその値をdst[i + N*vnum]
からdst[i + N*vnum + N - 1]
に書き戻している。
ベクトル値 load/store
srcポインタのベクトルに指定された位置からデータを読み、SVE型に格納する。
const svuint64_t src_base_ptrs = svindex_u64((uint64_t)(&src[i]), sizeof(float64_t));
svuint64_t dst_base_ptrs = svindex_u64((uint64_t)(&dst[i]), sizeof(float64_t));
const svfloat64_t data = svld1_gather_f64(pg, src_base_ptrs);
svst1_scatter(pg, dst_base_ptrs, data);
まず、svindex_u64
でsrc[i]
へのポインタをuint64_t
にキャストした値を頭にし、sizeof(float64_t)
のstepsizeを持つuint64_t
型のベクトル型を作っている。
つまり、
{(uint64_t)(&src[i]), (uint64_t)(&src[i]) + sizeof(float64_t), (uint64_t)(&src[i]) + 2 * sizeof(float64_t), ...}
というベクトル型を作っている。
svld1_gather
でsrc_base_ptrs[i]
からsrc_base_ptrs[i + N - 1]
までの要素をdata
に格納し、
svst1_scatter
でその値をdst[i]
からdst[i + N - 1]
に書き戻している。
続きそのうち書く
スカラー値 load/store、ベクトル値オフセット
srcポインタに指定された位置からoffsetベクトルに指定されたバイト数だけ間隔を開けてデータを読み、SVE型に格納する。
const svuint64_t byte_offsets = svindex_u64(0, sizeof(float64_t));
const svfloat64_t data = svld1_gather_offset(pg, &src[i], byte_offsets);
svst1_scatter_offset(pg, &dst[i], byte_offsets, data);
svld1_gather_offset
で、(float64_t*)((uint64_t)(&src[i]) + offset[i])
の値をdata[i]
に格納し、
svst1_scatter_offset
でその値を(float64_t*)((uint64_t)(&dst[i]) + offset[i])
に書き戻している。
おまけCPP
for(uint64_t i = 0 ; i < N ; i += stride){
const svbool_t pg = svwhilelt_b64(i, N);
const float64_t* const src_base_ptr = &src[i];
float64_t* const dst_base_ptr = &dst[i];
//const svfloat64_t data = svld1(pg, src_base_ptr);
//svst1(pg, dst_base_ptr, data);
//const uint64_t vnum = 0;
//const svfloat64_t data = svld1_vnum(pg, src_base_ptr, vnum);
//svst1_vnum(pg, dst_base_ptr, vnum, data);
//const svuint64_t src_base_ptrs = svindex_u64((uint64_t)(src_base_ptr), sizeof(float64_t));
//svuint64_t dst_base_ptrs = svindex_u64((uint64_t)(dst_base_ptr), sizeof(float64_t));
//const svfloat64_t data = svld1_gather_f64(pg, src_base_ptrs);
//svst1_scatter(pg, dst_base_ptrs, data);
//const svuint64_t byte_offsets = svindex_u64(0, sizeof(float64_t));
//const svfloat64_t data = svld1_gather_offset(pg, src_base_ptr, byte_offsets);
//svst1_scatter_offset(pg, dst_base_ptr, byte_offsets, data);
//const uint64_t byte_offset = 0;
//const svuint64_t src_base_ptrs = svindex_u64((uint64_t)(src_base_ptr), sizeof(float64_t));
//svuint64_t dst_base_ptrs = svindex_u64((uint64_t)(dst_base_ptr), sizeof(float64_t));
//const svfloat64_t data = svld1_gather_offset_f64(pg, src_base_ptrs, byte_offset);
//svst1_scatter_offset(pg, dst_base_ptrs, byte_offset, data);
const uint64_t base_index = 0;
const uint64_t step = 1;
const svuint64_t indices = svindex_u64(base_index, step);
const svfloat64_t data = svld1_gather_index(pg, src_base_ptr, indices);
svst1_scatter_index(pg, dst_base_ptr, indices, data);
}