読者です 読者をやめる 読者になる 読者になる

おぺんcv

画像処理エンジニアのブログ

ARM NEONの使い方 ロード・ストア編

ロード

vld1[q]_<type>(ptr)はptrから64bit(qが付く場合は128bit)のベクタをロードします

サンプル

符号付き16bit整数のベクタをロードしてみます
ロードしたベクタの各レーンをvget_lane_s16()で取得し、表示してみます

#include <stdio.h>
#include <stdint.h>
#include <arm_neon.h>

int main()
{
	int16_t a[4] = { 0, 1, 2, 3 };

	int16x4_t va = vld1_s16(a);

	printf("lane[0]: %d\n", vget_lane_s16(va, 0));
	printf("lane[1]: %d\n", vget_lane_s16(va, 1));
	printf("lane[2]: %d\n", vget_lane_s16(va, 2));
	printf("lane[3]: %d\n", vget_lane_s16(va, 3));

	return 0;
}
実行結果
lane[0]: 0
lane[1]: 1
lane[2]: 2
lane[3]: 3

ストア

vst1[q]_<type>(ptr, val)は64bit(qが付く場合は128bit)のベクタvalをptrにストアします

サンプル

vld1_s16()でロードしたベクタをもう一度ストアして表示してみます

#include <stdio.h>
#include <stdint.h>
#include <arm_neon.h>

int main()
{
	int16_t a[4] = { 0, 1, 2, 3 };

	int16x4_t va = vld1_s16(a);

	int16_t b[4];
	vst1_s16(b, va);

	for (int i = 0; i < 4; i++)
		printf("b[%d]: %d\n", i, b[i]);

	return 0;
}
実行結果
b[0]: 0
b[1]: 1
b[2]: 2
b[3]: 3

逆インターリーブロード

先ほどはvld1を紹介しました
実はこの他にvld2、vld3、vld4というものもあります

どんな処理になるのか、サンプルを見てみましょう

サンプル

vld2_s16()を使って、符号付き16bit整数のベクタをロードしてみます
vld2_s16()の戻り値はint16x4x2_tになります
これはデータ型編でも紹介した通り、int16x4_t を2つ要素に持つ型です
各要素はvalというメンバに格納されています
val[0]とval[1]それぞれのレーンを表示してみます

#include <stdio.h>
#include <stdint.h>
#include <arm_neon.h>

int main()
{
	int16_t a[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };

	int16x4x2_t va = vld2_s16(a);

	printf("val[0]:\n");
	printf("%d\n", vget_lane_s16(va.val[0], 0));
	printf("%d\n", vget_lane_s16(va.val[0], 1));
	printf("%d\n", vget_lane_s16(va.val[0], 2));
	printf("%d\n", vget_lane_s16(va.val[0], 3));

	printf("val[1]:\n");
	printf("%d\n", vget_lane_s16(va.val[1], 0));
	printf("%d\n", vget_lane_s16(va.val[1], 1));
	printf("%d\n", vget_lane_s16(va.val[1], 2));
	printf("%d\n", vget_lane_s16(va.val[1], 3));

	return 0;
}
実行結果
val[0]:
0
2
4
6
val[1]:
1
3
5
7

val[0]にはa[8]の偶数番目の要素が、val[1]にはa[8]の奇数番目の要素がロードされました
このサンプルの偶数と奇数のように、交互に配置されたものを分離することから
vld2(およびvld3、vld4)を逆インターリーブ(deinterleave)とも呼びます
(インターリーブには「交互配置する」という意味合いがあります)

逆インターリーブの実用例としては、以下のものがあります

  • XYの2次元座標の配列から、それぞれのチャンネル(X,Y)を分離する
  • RGBの画素値の配列から、それぞれのチャンネル(R,G,B)を分離する

インターリーブストア

こちらはデータをインターリーブ(交互配置)してストアする処理になります

サンプル

偶数の配列と奇数の配列をそれぞれint16x4x2_tのval[0]とval[1]にロードして
vst2_s16()でインターリーブしてみます

#include <stdio.h>
#include <stdint.h>
#include <arm_neon.h>

int main()
{
	int16_t a[4] = { 0, 2, 4, 6 }; //even
	int16_t b[4] = { 1, 3, 5, 7 }; //odd

	int16x4x2_t vc;
	vc.val[0] = vld1_s16(a);
	vc.val[1] = vld1_s16(b);

	int16_t c[8];
	vst2_s16(c, vc);

	for (int i = 0; i < 8; i++)
		printf("c[%d]: %d\n", i, c[i]);

	return 0;
}
実行結果
c[0]: 0
c[1]: 1
c[2]: 2
c[3]: 3
c[4]: 4
c[5]: 5
c[6]: 6
c[7]: 7

偶数と奇数を交互に配置することができました

参考

その他、インターリーブの解説がある記事・資料を載せておきます

次回

次回は加算編です
ARM NEONの使い方 加算編