SX-Aurora TSUBASAでvector intrinsicを使ってみる


## はじめに

NEC SX-Aurora TSUBASAのnc++にてベクトル命令のIntrinsicが解禁されたということで、早速使ってみるテスト。

とはいえ、マニュアル等もほとんどないので、教えてもらった情報や、webサーフィンしながら集めた情報を組み合わせて、なんとか動くところまで持っていきます。

※NECさんより正確な情報を頂くことができたので、一部を修正しました。ただし、当時の試行錯誤も自身にとっての記録として重要なので、最低限の修正に留めました。

## 必要な環境

コンパイラ ver 4.0.0以降が必要です。

## コードはどう書くのか

webで現状でみつかる`_vel_~~`で始まる命令は使えません。コンパイラ4.0.0以降では、`__builtin_ve_~~`で始まる命令になっています。

参考１：[https://sx-aurora-dev.github.io/ve-intrinsics-tutorial/](https://sx-aurora-dev.github.io/ve-intrinsics-tutorial/)

参考2: [https://speakerdeck.com/fixstars/c-plus-plus-kodofalse-sx-aurora-tsubasagao-su-hua-ji-fa-ji-jie-xue-xi-falseqian-chu-li-woti-cai-tosite?slide=40](https://speakerdeck.com/fixstars/c-plus-plus-kodofalse-sx-aurora-tsubasagao-su-hua-ji-fa-ji-jie-xue-xi-falseqian-chu-li-woti-cai-tosite?slide=40)

**追記：** 読める環境にいる方は、NEC C++マニュアルのver.4.0.0以降からはi
ntrinsicに関する情報が少し載っています。

また、Intrinsic用に何かヘッダーをインクルードする必要もありません（少なくとも今試している限りでは）。何もインクルードすることなく`__builtin_ve_~~`命令を書いてしまってコンパイルは通ります。

どんな命令があるのかは、コンパイラのインストールされたディレクトリの`include/_vector.h`の中に書いてあります。（注意点として、このヘッダーをインクルードしてはいけません。コンパイルが通りません。）

## サンプルプログラム

先にサンプルコードを載せた方がイメージしやすいと思うので、簡単な例を載せます。
```c++
#include <cstdio>

//the definition of vector type. 2048 means 8 byte x 256 //
typedef double __vr __attribute__((__vector_size__(2048)));  


int main(){
    constexpr int n = 256;

    double b[n]={0.0};

    for (int i = 0; i < n; ++i) {
        b[i] = (double)i;
    }

    //definition of vector type variable a//
    __vr a;

    //load to vector type a from double array b//
    __builtin_ve_vld(a, b, 8);

    // clear b
    for (int i = 0; i < n; ++i) {
        b[i] = 0.0;
    }

    //set from vector type a to double array b//
    __builtin_ve_vst(a, b, 8);

    //if the vector opeartion is correctly used, the numbers from 0.0 to 255.0 are printed.//
    for (int i = 0; i < n; ++i) {
        printf("b[%d] = %f\n", i, b[i]);
    }


    return 0;
}

```

これは、ベクトル型の変数aに、配列bから値をセットして、またaからbへ書き戻すだけのプログラムです。無事に書き戻せたことが分かるように、書き戻す前にbを0クリアしています。成功すれば0.0から255.0の値が表示されます。

## ベクトル変数の定義

ベクトル演算ではベクトル型の変数を用います。まずはベクトル型自体を次のように定義します
```c++
//the definition of vector type. 2048 means 8 byte x 256 //
typedef double __vr __attribute__((__vector_size__(2048)));  
```
サイズはバイト単位で指定するようで、8tyte×256=2048byteとなります。~~これ以外の大きさの方が使えるかは不明です。~~

**追記：** `__attribute__((__vector_size__(数字)))`での指定では2のべき乗数の指定が可能です。また、`__attribute__((ext_vector_type(数字)))`で指定することもでき、こちらは任意の数を指定できるようです(とはいえ256以下と思われる)。

実際にベクトル型の変数を確保するには、次のように定義します。
```c++
//definition of vector type variable a//
    __vr a;
```

## ベクトル変数への値の代入

事前に配列bに値を入れておいて、そこからベクトル型の変数へ代入(load)します。次の部分です。
```c++
//load to vector type a from double array b//
    __builtin_ve_vld(a, b, 8);
```
第三引数の8はストライドですが、いろいろ試してこの値が上手くいきました。経験則です。

## ベクトル変数からの値の読み取り

今度はベクトル変数aから配列bに値を書き戻します(set)。次の部分です。
```c++
//set from vector type a to double array b//
    __builtin_ve_vst(a, b, 8);
```
ここでも第三引数のストライドを8にしました。

## コンパイル

まずはコンパイラの4.0.0を有効化します。私の環境では次のようにします。
```
module load NECSDK-sx/4.0.0
```

あとは特に何も考えず、コンパイルします。ソースファイルを`test.cpp`としたとき、次のようにします。
```
nc++ test.cpp
```

## おわりに

webで出回っている命令と違っていたので、最初は何かインクルードしなきゃとか、velintrin.hが見つからないとかかなり混乱しましたが、なんとか動くところまで来ました。webの情報とは違ってclangも使わないようです。

**追記：**clangは使わないとオフィシャルな回答を頂きました。

徐々に慣れていけそうです。

これでコンパイラとエスパー対決しなくてよくなると思うと、かなり気が楽になります。

メリークリスマス！