プログラミング事例

ベクトル型の利用

ベクトル型の定数を記述するのは大変であるため、一般的には共用体(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;
}