ARM NEON:常规 C 代码在简单乘法中比 ARM Neon 代码快?
ARM NEON: Regular C code is faster than ARM Neon code in simple multiplication?
我正在使用 ARM NEON 内部函数为数组实现一个简单的乘法。输入是 uint8 数组,输出是 uint16 数组。但是,常规本机代码比 NEON 优化代码更快。谁能帮我弄清楚如何改进 NEON 代码?
我的常规代码是
uint16_t scale_factor = 300;
for(int i = 0; i < output_size; i++)
{
out_16bit[i] = (uint16_t)(in_ptr[i] * scale_factor) ;
}
我的 NEON 代码是
uint16_t* out_ptr = out_16bit;
uint8_t* in_ptr = in_8bit;
uint16_t scale_factor = 300;
for(int i = 0; i < out_size/16; i++)
{
uint8x16_t in_v0 = vld1q_u8(in_ptr);
in_ptr += 16;
uint16x8_t in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
uint16x8_t in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
uint16x8_t res_0 = vmulq_n_u16(in_16_v0, scale_factor);
uint16x8_t res_1 = vmulq_n_u16(in_16_v1, scale_factor);
// code below takes long time
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
out_ptr += 16;
}
我也做了一些分析,发现如果注释掉vst1q_u16
s或out_ptr += 16
,速度很快。但是如果我像上面那样保留两者,它会很慢。所以我猜可能是因为指针的增量在等待vst1q_u16
的完成?然后我更新了 NEON 代码,在 vst1q_u16
和 out_ptr+=16
之间添加了一些代码,如下所示,
uint8x16_t in_v0 = vld1q_u8(in_ptr);
uint16x8_t in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
uint16x8_t in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
uint16x8_t res_0 = vmulq_n_u16(in_16_v0, scale_factor);
uint16x8_t res_1 = vmulq_n_u16(in_16_v1, scale_factor);
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
for(int i = 1; i < out_size/16; i++)
{
in_v0 = vld1q_u8(in_ptr);
in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
out_ptr += 16;
res_0 = vmulq_n_u16(in_16_v0, scale_factor);
res_1 = vmulq_n_u16(in_16_v1, scale_factor);
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
}
但是这个改变没有用...请帮忙指教我应该怎么做...谢谢。
如评论中所述,简单的答案是自动矢量化。
我不确定是否适用于 clang 6,但肯定会在针对 Neon 平台时默认情况下更新的 clang 自动矢量化为 Neon,并且很难在像这种乘法这样简单的事情上击败自动矢量化。也许为您的特定处理器展开最佳循环。但是很容易比自动矢量化差。 Godbolt 是一种非常好的比较方式,可以分析您的所有更改。
所有的评论也都很有道理。
有关 Neon 内部函数最佳实践的更多文档,请访问 Arm 的 Neon microsite has very useful information, especially the doc on Optimizing C with Neon intrinsics。
我正在使用 ARM NEON 内部函数为数组实现一个简单的乘法。输入是 uint8 数组,输出是 uint16 数组。但是,常规本机代码比 NEON 优化代码更快。谁能帮我弄清楚如何改进 NEON 代码?
我的常规代码是
uint16_t scale_factor = 300;
for(int i = 0; i < output_size; i++)
{
out_16bit[i] = (uint16_t)(in_ptr[i] * scale_factor) ;
}
我的 NEON 代码是
uint16_t* out_ptr = out_16bit;
uint8_t* in_ptr = in_8bit;
uint16_t scale_factor = 300;
for(int i = 0; i < out_size/16; i++)
{
uint8x16_t in_v0 = vld1q_u8(in_ptr);
in_ptr += 16;
uint16x8_t in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
uint16x8_t in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
uint16x8_t res_0 = vmulq_n_u16(in_16_v0, scale_factor);
uint16x8_t res_1 = vmulq_n_u16(in_16_v1, scale_factor);
// code below takes long time
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
out_ptr += 16;
}
我也做了一些分析,发现如果注释掉vst1q_u16
s或out_ptr += 16
,速度很快。但是如果我像上面那样保留两者,它会很慢。所以我猜可能是因为指针的增量在等待vst1q_u16
的完成?然后我更新了 NEON 代码,在 vst1q_u16
和 out_ptr+=16
之间添加了一些代码,如下所示,
uint8x16_t in_v0 = vld1q_u8(in_ptr);
uint16x8_t in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
uint16x8_t in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
uint16x8_t res_0 = vmulq_n_u16(in_16_v0, scale_factor);
uint16x8_t res_1 = vmulq_n_u16(in_16_v1, scale_factor);
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
for(int i = 1; i < out_size/16; i++)
{
in_v0 = vld1q_u8(in_ptr);
in_16_v0 = vmovl_u8(vget_low_u8(in_v0));
in_16_v1 = vmovl_u8(vget_high_u8(in_v0));
out_ptr += 16;
res_0 = vmulq_n_u16(in_16_v0, scale_factor);
res_1 = vmulq_n_u16(in_16_v1, scale_factor);
vst1q_u16(out_ptr,res_0);
vst1q_u16(out_ptr+8,res_1);
}
但是这个改变没有用...请帮忙指教我应该怎么做...谢谢。
如评论中所述,简单的答案是自动矢量化。 我不确定是否适用于 clang 6,但肯定会在针对 Neon 平台时默认情况下更新的 clang 自动矢量化为 Neon,并且很难在像这种乘法这样简单的事情上击败自动矢量化。也许为您的特定处理器展开最佳循环。但是很容易比自动矢量化差。 Godbolt 是一种非常好的比较方式,可以分析您的所有更改。
所有的评论也都很有道理。
有关 Neon 内部函数最佳实践的更多文档,请访问 Arm 的 Neon microsite has very useful information, especially the doc on Optimizing C with Neon intrinsics。