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

おぺんcv

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

ARM NEONの使い方 乗算編

ARM

今回は乗算編です
特に変わった内容はありません

乗算

vmul[q]_<type>(va, vb)

64bit(qが付く場合は128bit)のベクタvaとvbの乗算

サンプル

符号付き16bit整数ベクタの乗算をしてみます

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

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

	int16x4_t va = vld1_s16(a);
	int16x4_t vb = vld1_s16(b);

	int16x4_t vc = vmul_s16(va, vb);

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

	return 0;
}
実行結果
c[0]: 5
c[1]: 12
c[2]: 21
c[3]: 32

整数を使っていて桁あふれを考慮する場合はvmull(Vector long multiply)を使いましょう

積和演算

みんな大好き(?)積和演算
内積とか計算するときに重宝しますね

vmla[q]_<type>(va, vb, vc)

64bit(qが付く場合は128bit)のベクタva、vb、vcについて

va + vb * vc

を計算します

サンプル

符号付き16bit整数のベクタの積和演算です

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

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

	int16x4_t va = vld1_s16(a);
	int16x4_t vb = vld1_s16(b);
	int16x4_t vc = vld1_s16(c);

	int16x4_t vd = vmla_s16(va, vb, vc);

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

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

doubling multiplyってなんぞや

乗算に関しては先ほどのmulとmlaを使うことがほとんどだと思いますが
命令一覧を見ると doubling multiply というのがあったので使ってみました

サンプル

vqdmull(Vector saturating doubling long multiply)を使って乗算をしてみると…

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

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

	int16x4_t va = vld1_s16(a);
	int16x4_t vb = vld1_s16(b);

	int32x4_t vc = vqdmull_s16(va, vb);

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

	return 0;
}
実行結果
c[0]: 10
c[1]: 24
c[2]: 42
c[3]: 64

結果が2倍になった
どうやら乗算してさらに2倍する演算のようです
使い道はあるのかな…

次回

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