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_u16s或out_ptr += 16,速度很快。但是如果我像上面那样保留两者,它会很慢。所以我猜可能是因为指针的增量在等待vst1q_u16的完成?然后我更新了 NEON 代码,在 vst1q_u16out_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