プログラミング事例¶
ベクトル型の利用¶
ベクトル型の定数を記述するのは大変であるため、一般的には共用体(union)を利用して初期化します。
#include <iostream> // C++のエリアステンプレートを使ったベクトル型の型定義 template <typename T, int N> using vector_type = T __attribute__((ext_vector_type(N))); // 初期値設定、要素の設定、取り出しを容易にするためのunionの宣言 template <typename T, int N> union Vec { vector_type<T, N> v; T e[N]; }; // ベクトルマスクのためのunionの宣言 typedef union { __vm v; unsigned int m[8]; } vmask; template <typename T, int N> T vector_sum(Vec<T, N> &a, Vec<T, N> &b, vmask &vm) { vector_type<T, N> c; c = a.v * b.v; __builtin_ve_vfsum(c, c, vm.v); // Vector builtin関数により総和を計算 return c[0]; // 計算結果は0番目の要素に設定される } int main() { double sum; vmask vm; Vec<double, 64> a, b; // ベクトル型の初期値設定 for (int i = 0; i < 64; i++) { a.e[i] = i; b.e[i] = 64 - i; } // ベクトルマスク値の設定 for (int i = 0; i < 8; i++) { vm.m[i] = 0xAAAAAAAAU; // 0b10101010101010.... } sum = vector_sum<double,64>(a, b, vm); std::cout << sum << " (Result must be 21824)" << std::endl; }
ベクトル型の初期値設定¶
ベクトル型の定数を使用する。
#include <stdio.h> typedef int v4 __attribute__((ext_vector_type(4))); main() { v4 v = {1, 2, 3, 4}; // ベクトル型の定数 v = v * 2; for (int i = 0; i < 4; i++) { printf(" %d\n", v[i]); } }
配列からベクトル型への値のコピー¶
配列要素ごとにコピーする。(効率がよくないので非推奨)
#include <stdio.h> typedef int v16 __attribute__((ext_vector_type(16))); main() { int idx[4]; v4 v; for (int i = 0; i < 16; i++) { // 配列への初期値設定 idx[i] = i; } for (int i = 0; i < 16; i++) { v[i] = idx[i]; // ベクトル型の [] 演算子により要素ごとに代入 } for (int i = 0; i < 16; i++) { printf(" %d\n", v[i]); } }
Vector Builtin関数を利用してコピーする。
#include <stdio.h> typedef int v16 __attribute__((ext_vector_type(16))); int idx[16]; main() { v16 v; for (int i = 0; i < 16; i++) { // 配列の初期値設定 idx[i] = i; } __builtin_ve_vld(v, idx, sizeof(int)); // Vector Builtin関数の呼び出し // ベクトルマスク、ベクトル長の指定は省略可 for (int i = 0; i < 16; i++) { printf(" %d\n", v[i]); } }
unionを使用し、配列とメモリ領域をシェアしておく。
#include <stdio.h> typedef int v16 __attribute__((ext_vector_type(16))); typedef union { int idx[16]; v16 v; } U; main() { U u; for (int i = 0; i < 16; i++) { // 配列の初期値設定 u.idx[i] = i; } for (int i = 0; i < 16; i++) { printf(" %d\n", u.v[i]); } }
ベクトルマスク型の初期値設定¶
ベクトルマスク型の定数を使用する。
#include <stdio.h> main() { __vm m = { 0xFFFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL, 0xFFFFFFFFFFFFFFFFUL }; // すべてのビットが1 int popcount = __builtin_ve_pcvm(m); // 値が1のビット数を取得 printf("%d\n", popcount); }
否定演算子を適用する。
#include <stdio.h> main() { __vm m1 = { 0 }; // すべてのビットが 0 __vm m2 = ~m1; // 否定演算ですべてのビットを1に反転 int popcount; popcount = __builtin_ve_pcvm(m2); // 値が1のビット数を取得 printf("%d\n", popcount); }
Vector Builtin関数の活用:ベクトルループ¶
#include <stdio.h> typedef double v256 __attribute__((ext_vector_type(256))); #define N 500 double a[N], b[N], c[N]; main() { v256 x, y, z; int vl; for (int i = 0; i < N; i++) { a[i] = 0.0; b[i] = c[i] = (double) i; } // for (int i = 0; i < N; i++) { // a[i] = b[i] + c[i]; // } vl = N % 256; for (int i = 0; i < N; ) { // ベクトルループ __builtin_ve_vld( y, &(b[i]), sizeof(double), vl); __builtin_ve_vld( z, &(c[i]), sizeof(double), vl); __builtin_ve_vfadd(x, y, z, vl); __builtin_ve_vst( x, &(a[i]), sizeof(double), vl); i += vl; vl = 256; } for (int i = 0; i < N; i++) { printf("%f\n", a[i]); } }
Vector Builtin関数の活用:IF条件を含むベクトルループ (ベクトルマスク使用)¶
#include <stdio.h> #include <_vector.h> // _GE の参照のためインクルード typedef double v256 __attribute__((ext_vector_type(256))); #define N 500 double a[N], b[N], c[N]; main() { v256 x, y, z, cmp; __vm mask = { 0 }; // ベクトルマスク型の宣言、初期化 int vl; for (int i = 0; i < N; i++) { a[i] = 0.0; b[i] = (double) i; c[i] = (double) (N - i); } // for (int i = 0; i < N; i++) { // if (b[i] >= c[i]) { // a[i] = b[i] - c[i]; // } else { // a[i] = c[i] - b[i]; // } // } vl = N % 256; // "256" は VE の最大ベクトル長 for (int i = 0; i < N; ) { // ベクトルループ __builtin_ve_vld( y, &(b[i]), sizeof(double), vl); __builtin_ve_vld( z, &(c[i]), sizeof(double), vl); __builtin_ve_vfcmp(cmp, y, z, vl); // b, c の比較 __builtin_ve_vfmk( mask, cmp, _GE, vl); // >= が真であるビットが1のマスク __builtin_ve_vfsub(x, y, z, mask, vl); // b - c __builtin_ve_negm( mask, mask); // >= が偽であるビットが1のマスク __builtin_ve_vfsub(x, z, y, mask, vl); // c - b __builtin_ve_vst( x, &(a[i]), sizeof(double), vl); i += vl; vl = 256; // 初回以降は最大ベクトル長で処理する } for (int i = 0; i < N; i++) { printf("%f\n", a[i]); } }
Vector Builtin関数の活用:リダクション演算¶
#include <cstdio> #include <random> using namespace::std; template <typename T, int N> using vector_type = T __attribute__((ext_vector_type(N))); extern double *random_vector(int n) // 乱数列の生成 { random_device rd; mt19937 mt(rd()); uniform_real_distribution<double> u(0.0, 10.0); double *v = new double[n]; for (int i = 0; i < n; i++) { v[i] = u(mt); } return v; } int main() { double *r = random_vector(256); vector_type<double, 256> v, a, b, c; __vm mask; __builtin_ve_vld(v, r, sizeof(double)); __builtin_ve_vfsum(a, v); // 総和冤罪 printf("sum = %f\n", a[0]); __builtin_ve_vfrmax(b, v, 0 /*_FIRST*/); // 最大値の取得 printf("max (<=10.0) = %f, pos = %d \n", b[0], b[4]); __builtin_ve_vfcmp(c, v, 5.0); __builtin_ve_vfmk( mask, c, 5 /*_LE*/); // 値が5.0以下の要素 __builtin_ve_vfrmax(b, v, 0 /*_FIRST*/, mask); // 5.0以下の最大値の取得 printf("max (<= 5.0) = %f, pos = %d \n", b[0], b[4]); return 0; }