使用NEON SIMD进行Float和反向转换的简称

时间:2017-05-17 21:30:08

标签: android arm simd neon

我在Android处理音频缓冲区,我的设置如下:

  1. 使用短缓冲区获取系统回调
  2. 将短缓冲区转换为浮动缓冲区
  3. 使用浮动缓冲区做一些DSP
  4. 将浮动缓冲区转换为短缓冲区
  5. 向系统提供短缓冲区
  6. 我希望减少第2步和第4步的延迟,以及浮动和浮动到短转换的延迟。 (撇开3-DSP中的延迟,因为我稍后会处理)。

    所以,我想使用NEON SIMD一次计算多个值。

    我目前对2和4的内容是以下代码:

    #define CONV16BIT 32768
    #define CONVMYFLT (1./32768.)
    static int i;
    float * floatBuffer;
    short * shortInBuffer;
    short * shortOutBuffer;
    
    ...(malloc and init buffers method)
    
    ...(inside callback) 
    //2- short to float
    for(i = 0; i < bufferSize; i++) {
        floatBuffer[i] = (float) (shortInBuffer[i] * CONVMYFLT);
    }
    
    ...(do dsp)
    
    //4- float to short
    for(i = 0; i < bufferSize; i++) {
        shortOutBuffer[i] = (short) (floatBuffer[i] * CONV16BIT);
    }
    

    我认为利用NEON所需的步骤是:

    (对于浮动部分的短路)

    1. 从短缓冲区加载16位短路
    2. 将它们转换为32位整数
    3. 将它们转换为float
    4. 乘以CONVMYFLT
    5. 将它们存储到浮动缓冲区
    6. 在此post(已选择的答案)中找到此信息

      __m128 factor = _mm_set1_ps(1.0f / value);
      for (int i = 0; i < W*H; i += 8)
      {
          //  Load 8 16-bit ushorts.
          //  vi = {a,b,c,d,e,f,g,h}
          __m128i vi = _mm_load_si128((const __m128i*)(source + i));
      
          //  Convert to 32-bit integers
          //  vi0 = {a,0,b,0,c,0,d,0}
          //  vi1 = {e,0,f,0,g,0,h,0}
          __m128i vi0 = _mm_cvtepu16_epi32(vi);
          __m128i vi1 = _mm_cvtepu16_epi32(_mm_unpackhi_epi64(vi,vi));
      
          //  Convert to float
          __m128 vf0 = _mm_cvtepi32_ps(vi0);
          __m128 vf1 = _mm_cvtepi32_ps(vi1);
      
          //  Multiply
          vf0 = _mm_mul_ps(vf0,factor);
          vf1 = _mm_mul_ps(vf1,factor);
      
          //  Store
          _mm_store_ps(destination + i + 0,vf0);
          _mm_store_ps(destination + i + 4,vf1);
      }
      

      然而,对于英特尔SSE4.1而言,这不是NEON的SIMD。

      Android中NEON的等效实现是什么?(很难理解NEON内在函数)

      更新1 从fsheikh的答案,我能够建立这个: - 我能够从系统回调中获取int16_t - 我的所有缓冲区大小都是8的倍数:

      int16x8_t i16v;
      int32x4_t i32vl, i32vh;
      float32x4_t f32vl, f32vh;
      for(i = 0; i < bufferSize; i += 8) {
          //load 8 16-bit lanes on vector
          i16v = vld1q_s16((const int16x8_t*) int16_t_inBuffer[i]);
          // convert into 32-bit signed integer
          i32vl = vmovl_s16 (i16v);
          i32vh = vmovl_s16 (vzipq_s16(i16v, i16v).val[0]);
          //convert to 32-bit float
          f32vl = vcvtq_f32_s32(i32vl);
          f32vh = vcvtq_f32_s32(i32vh);
          //multiply by scalar
          f32vl = vmulq_n_f32(f32vl, CONVMYFLT);
          f32vh = vmulq_n_f32(f32vh, CONVMYFLT);
          //store in float buffer
          vst1q_f32(floatBuffer[i], f32vl);
          vst1q_f32(floatBuffer[i + 4], f32vh);
      }
      

      这应该正常吗? 我怀疑我应该使用vmovl_s16返回的交错向量的低或高部分:

      i32vh = vmovl_s16(vzipq_s1 6(i16v,i16v).val [0]);或

      i32vh = vmovl_s16(vzipq_s16(i16v,i16v).val [1]);

1 个答案:

答案 0 :(得分:3)

获得SSE版本后,可以使用GCC ARM NEON内在列表将SSE宏移植到NEON。 https://gcc.gnu.org/onlinedocs/gcc-4.6.4/gcc/ARM-NEON-Intrinsics.html

例如:

// Load unsigned short
uint16x4_t vld1_u16 (const uint16_t *)

// Convert to unsigned int
uint32x4_t vmovl_u16 (uint16x4_t) 

// Convert to float
float32x4_t vcvtq_f32_s32 (int32x4_t)

// Multiply floats with a scalar
float32x4_t vmulq_n_f32 (float32x4_t, float32_t) 

// Store results into a float buffer
void vst1q_f32 (float32_t *, float32x4_t)