如何使用 SIMD 优化 for 循环的计算?
How to optimize the computation of a for loop using SIMD?
我正在尝试使用 Neon SIMD 在 ODROID XU4 ARM 平台上加速立体匹配算法。为此,我正在使用 openMp
编译指示。
void StereoMatch:: sadCol(uint8_t* leftRank,uint8_t* rightRank,const int SAD_WIDTH,const int SAD_WIDTH_STEP, const int imgWidth,int j, int d , uint16_t* cost)
{
uint16_t sum = 0;
int n = 0;
int m =0;
for ( n = 0; n < SAD_WIDTH+1; n++)
{
#pragma omp simd
for( m = 0; m< SAD_WIDTH_STEP; m = m + imgWidth )
{
sum += abs(leftRank[j+m+n]-rightRank[j+m+n-d]);
};
cost[n] = sum;
sum = 0;
};
我是 SIMD 和 openMp 的新手,我知道在代码中使用 SIMD pragma 会指示编译器对减法进行向量化,但是当我执行代码时,我没有发现任何区别。我应该在我的代码中添加什么以对其进行矢量化?
如评论中所述,ARM-Neon 有一条指令可以直接执行您想要的操作,即计算无符号字节的绝对差并将其累加为无符号字节 short-integers。
假设 SAD_WIDTH+1==8
,这是一个使用内部函数的非常简单的实现(基于@nemequ 的简化版本):
void sadCol(uint8_t* leftRank,
uint8_t* rightRank,
int j,
int d ,
uint16_t* cost) {
const int SAD_WIDTH = 7;
const int imgWidth = 320;
const int SAD_WIDTH_STEP = SAD_WIDTH * imgWidth;
uint16x8_t cost_8 = {0};
for(int m = 0; m < SAD_WIDTH_STEP; m = m + imgWidth ) {
cost_8 = vabal_u8(cost_8, vld1_u8(&leftRank[j+m]), vld1_u8(&rightRank[j+m-d]));
};
vst1q_u16(cost, cost_8);
};
vld1_u8
加载8个连续字节,vabal_u8
计算绝对差并累加到第一个寄存器。最后vst1q_u16
将寄存器存入内存
您可以轻松制作imgWidth
和SAD_WIDTH_STEP
函数参数。如果 SAD_WIDTH+1
是 8 的不同倍数,您可以为此编写另一个循环。
我手头没有 ARM 平台来测试它,但是 "it compiles": https://godbolt.org/z/vPqiYI(在我看来,程序集看起来不错)。如果您使用 -O3
进行优化,gcc 将展开循环。
我正在尝试使用 Neon SIMD 在 ODROID XU4 ARM 平台上加速立体匹配算法。为此,我正在使用 openMp 编译指示。
void StereoMatch:: sadCol(uint8_t* leftRank,uint8_t* rightRank,const int SAD_WIDTH,const int SAD_WIDTH_STEP, const int imgWidth,int j, int d , uint16_t* cost)
{
uint16_t sum = 0;
int n = 0;
int m =0;
for ( n = 0; n < SAD_WIDTH+1; n++)
{
#pragma omp simd
for( m = 0; m< SAD_WIDTH_STEP; m = m + imgWidth )
{
sum += abs(leftRank[j+m+n]-rightRank[j+m+n-d]);
};
cost[n] = sum;
sum = 0;
};
我是 SIMD 和 openMp 的新手,我知道在代码中使用 SIMD pragma 会指示编译器对减法进行向量化,但是当我执行代码时,我没有发现任何区别。我应该在我的代码中添加什么以对其进行矢量化?
如评论中所述,ARM-Neon 有一条指令可以直接执行您想要的操作,即计算无符号字节的绝对差并将其累加为无符号字节 short-integers。
假设 SAD_WIDTH+1==8
,这是一个使用内部函数的非常简单的实现(基于@nemequ 的简化版本):
void sadCol(uint8_t* leftRank,
uint8_t* rightRank,
int j,
int d ,
uint16_t* cost) {
const int SAD_WIDTH = 7;
const int imgWidth = 320;
const int SAD_WIDTH_STEP = SAD_WIDTH * imgWidth;
uint16x8_t cost_8 = {0};
for(int m = 0; m < SAD_WIDTH_STEP; m = m + imgWidth ) {
cost_8 = vabal_u8(cost_8, vld1_u8(&leftRank[j+m]), vld1_u8(&rightRank[j+m-d]));
};
vst1q_u16(cost, cost_8);
};
vld1_u8
加载8个连续字节,vabal_u8
计算绝对差并累加到第一个寄存器。最后vst1q_u16
将寄存器存入内存
您可以轻松制作imgWidth
和SAD_WIDTH_STEP
函数参数。如果 SAD_WIDTH+1
是 8 的不同倍数,您可以为此编写另一个循环。
我手头没有 ARM 平台来测试它,但是 "it compiles": https://godbolt.org/z/vPqiYI(在我看来,程序集看起来不错)。如果您使用 -O3
进行优化,gcc 将展开循环。