使用SSE内在函数存储四个16位整数

时间:2014-02-26 12:51:51

标签: c++ sse intrinsics sse2

我乘以四个32位浮点数,然后用SSE内在函数将其转换为四个16位整数。我想将四个整数结果存储到一个数组中。使用花车很容易:_mm_store_ps(float_ptr, m128value)。但是,我还没有找到任何使用16位(__m64)整数执行此操作的指令。

void process(float *fptr, int16_t *sptr, __m128 factor)
{
  __m128 a = _mm_load_ps(fptr);
  __m128 b = _mm_mul_ps(a, factor);
  __m128 c = _mm_round_ps(b, _MM_FROUND_TO_NEAREST_INT);
  __m64 s =_mm_cvtps_pi16(c);
  // now store the values to sptr
}

任何帮助都将不胜感激。

3 个答案:

答案 0 :(得分:3)

我个人会避免使用MMX。此外,我会使用显式存储而不是隐式存储,它通常只适用于某些编译器。以下代码适用于MSVC2012和SSE 4.1。

请注意,fptr需要16字节对齐。如果您在64位模式下编译但在32位模式下,则应确保它已对齐。

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

void process(float *fptr, int16_t *sptr, __m128 factor)
{
  __m128 a = _mm_load_ps(fptr);
  __m128 b = _mm_mul_ps(a, factor);
  __m128i c = _mm_cvttps_epi32(b);
  __m128i d = _mm_packs_epi32(c,c);
  _mm_storel_epi64((__m128i*)sptr, d);
}

int main() {
    float x[] = {1.0, 2.0, 3.0, 4.0};
    int16_t y[4];
    __m128 factor = _mm_set1_ps(3.14159f);
    process(x, y, factor);
    printf("%d %d %d %d\n", y[0], y[1], y[2], y[3]);
}

请注意,_mm_cvtps_pi16不是一个简单的内在因素,英特尔内部指南说“这个内在创建了两个或更多指令的序列,并且可能比本机指令表现更差。考虑这个内在的性能影响。”

以下是使用MMX版本的装配输出

mulps   (%rdi), %xmm0
roundps $0, %xmm0, %xmm0
movaps  %xmm0, %xmm1
cvtps2pi    %xmm0, %mm0
movhlps %xmm0, %xmm1
cvtps2pi    %xmm1, %mm1
packssdw    %mm1, %mm0
movq    %mm0, (%rsi)
ret

以下是仅使用SSE版本的装配输出

mulps   (%rdi), %xmm0
cvttps2dq   %xmm0, %xmm0
packssdw    %xmm0, %xmm0
movq    %xmm0, (%rsi)
ret

答案 1 :(得分:2)

使用__m64类型,您可以恰当地转换目标指针:

void process(float *fptr, int16_t *sptr, __m128 factor)
{
  __m128 a = _mm_load_ps(fptr);
  __m128 b = _mm_mul_ps(a, factor);
  __m128 c = _mm_round_ps(b, _MM_FROUND_TO_NEAREST_INT);
  __m64 s =_mm_cvtps_pi16(c);
  *((__m64 *) sptr) = s;
}

对齐和未对齐的商店与MMX指令没有区别,就像SSE / AVX一样;因此,您不需要内在函数来执行商店。

答案 2 :(得分:1)

我认为您可以安全地将其移动到一般的64位寄存器(long long将适用于Linux LLP64和Windows LP64)并自行复制。

根据我在xmmintrin.h中所读到的内容,gcc可以完美地处理从__m64long long的演员表。 当然,您可以使用_mm_cvtsi64_si64x

short* f;
long long b = _mm_cvtsi64_si64x(s);
f[0] = b >> 48;
f[1] = b >> 32 & 0x0000FFFFLL;
f[2] = b >> 16 & 0x000000000FFFFLL;
f[3] = b & 0x000000000000FFFFLL;

您可以使用联合键入pune以使其看起来更好,但我想这将属于未定义的行为。